zoukankan      html  css  js  c++  java
  • 字符串板子

     上板子

    KMP:

        for(int i=1;i<n;i++){
            while(u>0&&s[u]!=s[i]){
                u=Pi[u-1];
            }
            if(s[u]==s[i]){
                u++;
            }
            Pi[i]=u;
        }
    KMP

    Z Algorithm:

        for(int i=1,l=0,r=0;i<len;i++){
            if(i<=r)Z[i]=min(r-i+1,Z[i-l]);
            else Z[i]=0;
            while(s1[Z[i]]==s1[i+Z[i]])Z[i]++;
            if(i+Z[i]>r){
                for(int j=r+1;j<i+Z[i];j++)Pi[j]=j-i+1;
                r=i+Z[i]-1,l=i;
            }
        }
    Z Algorithm

    Z函数与KMP的各种用法:

    //查找子串t在s中出现的位置 
        int u = 0;
        for(int i=1;i<m;i++){
            while(u>0&&t[u]!=t[i])u=Pi[u];
            if(t[u]==t[i])u++;
            Pi[i]=u;
        }
        u=0;
        for( int i = 0;i < n; i++){
            while(u>0&&t[u]!=s[i])u=Pi[u];
            if(t[u]==s[i])u++;
            if(u==m){
                printf("%d
    ",i-m+1);
                u=Pi[u];
            }
        }
        //查找每个前缀的出现次数 
        KMP(s,n,Pi);
        for (int i = 0; i < n; i++) ans[pi[i]]++;
        for (int i = n - 1; i > 0; i--) ans[pi[i - 1]] += ans[i];
        for (int i = 0; i <= n; i++) ans[i]++; 
        //查找本质不同子串数目 
        //注意 KMP的查找本质不同子串是O(n方)的 不如后缀数组 
        for(int i=0;i<n;i++){
            char c;int MAX=0;
            cin>>c;
            strcat(s,c);s1=s;
            reverse(s1,s1+i+1);
            KMP(s1,i+1,Pi);
            for(int j=0;j<=i;j++)MAX=max(MAX,s1[j]);
            ans+=(i-MAX);
        }
        //查找b与a的每一个后缀的最长公共前缀 
        scanf("%s%s",s2,s1);
        int len1=strlen(s1);
        int len2=strlen(s2);
        strcat(s1,"#");
        strcat(s1,s2);
        int len=len1+len2,l=0,r=0;
        for(int i=1;i<=len;i++){
            if(i<=r)z[i]=min(r-i+1,z[i-l]);
            else z[i]=0;
            while(s1[z[i]]==s1[i+z[i]])z[i]++;
            if(i+z[i]>r)r=i+z[i]-1,l=i;
        }
        for(int i=0;i<len1;i++){
            if(!i)printf("%d ",len1);
            else printf("%d ",z[i]);
        }
        puts("");
        for(int i=len1+1;i<=len;i++){
            printf("%d ",z[i]);
        }
        //字符串压缩
        Z Algorithm(s,n,Z);
        for(int i=1;i<n;i++){
            if((n%i==0)&&(z[i]+i==n)){
                printf("%d ",i);
            }
        }//Z
        puts("");
        KMP(s1,n,Pi);
        printf("%d",n-Pi[n-1]);//KMP
    KMP Z Altorithm

    后缀数组:

    void Qsort(){//基数排序 
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)c[x[i]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
    }
    void get_SA(){//得到后缀数组 sa[i](排名第i小的后缀) 
        for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i;
        Qsort();
        for(int k=1;k<=n;k<<=1){
            int num=0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            Qsort();
            swap(x,y);
            x[sa[1]]=1,num=1;
            for(int i=2;i<=n;i++){
                if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]){
                    x[sa[i]]=num;
                }else{
                    x[sa[i]]=++num;
                }
            }
            if(num==n)break;
            m=num;
        }
        for(int i=1;i<=n;i++){
            printf("%d ",sa[i]);
        }
    }
    //lcp(x,y):字符串x与字符串y的最长公共前缀,在这里指x号后缀与与y号后缀的最长公共前缀
    void get_height(){//得到height数组 height[i] ( lcp(sa[i],sa[i-1]) ,即排名为i的后缀与排名为i-1的后缀的最长公共前缀) 
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++){
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
        for(int i=1;i<=n;i++){
            printf("%d ",height[i]);
        }
    }
    /*  一些height的用法
        1. 两个后缀的最大公共前缀  :  lcp(x,y)=min(height[i]),i∈[x,y],用st表维护,O(1)查询
        2.可重叠最长重复字串         :height数组里的最大值
        3.不可重叠最长重复子串     : 二分答案x maxsa[i],minsa[i],枚举排名第i的后缀 若height[i]>=x 更新maxsa[i]与minsa[i]若(max-min)>=x就return 1 
        4.本质不同的子串数量     :  枚举每一个后缀,第i个后缀对答案的贡献为len-sa[i]+1-height[i]
    */
    SA
    SA

    自动机(们):

    AC自动机:

    void insert(char *s,int id){//trie树的插入
        int now=0;
        for(int i=0;s[i];i++){
            if(!trie[now][s[i]-'a'])trie[now][s[i]-'a']=++tot;
            now=trie[now][s[i]-'a'];
        }
        match[id]=now;
    }
    void build(){//建trie图 个人理解 trie图=trie树+fail边 
        for(int i=0;i<26;i++){
            if(trie[0][i]){
                q.push(trie[0][i]);
            }
        }
        while(!q.empty()){
            int from=q.front();
            q.pop();
            for(int i=0;i<26;i++){
                if(trie[from][i]){
                    fail[trie[from][i]]=trie[fail[from]][i];
                    q.push(trie[from][i]);
                }else{
                    trie[from][i]=trie[fail[from]][i];
                }
            }
        }
    }
    void solve(){//查找t在s中出现次数
        scanf("%s", s);
        for(int i=0,now=0;s[i];i++){//记录路径
            now=trie[now][s[i]-'a'];
            siz[now]++;
        }
        for(int i=1;i<=tot;i++){//建fail树
            add(fail[i],i);
        }
        dfs(0);//求出每个t串在Trie树上的终止节点在fail树上的子树总匹配次数 
        for(int i=1;i<=n;i++){
            printf("%d
    ",siz[match[i]]);
        }
    }
    AC自动机

    回文自动机:

    int get_fail(char *s,int X,int pos){//得到fail(小于自己长度的最长回文后缀) 
        while(pos-len[X]-1<0||s[pos-len[X]-1]!=s[pos]){
            X=fail[X];
        }
        return X;
    }
    int get_tran(char *s,int X,int pos,int tot){//得到tran  
        while(pos-len[X]-1<0||s[pos-len[X]-1]!=s[pos]||((len[X]+2)*2>len[tot]))X=fail[X];
        return X;
    }
    void PAM(char *s){//建回文自动机 
        len[0]=0,len[1]=-1;
        fail[0]=1,fail[1]=0;
        for(int i=0;s[i];i++){
            int cur=get_fail(s,last,i);
            if(!ch[cur][s[i]-'a']){//新的回文子串不在自动机中 
                fail[++tot]=ch[get_fail(s,fail[cur],i)][s[i]-'a'];//建新节点 先连fail边 
                len[tot]=len[cur]+2;//更新节点的长度 
                sum[tot]=sum[fail[tot]]+1;//更新与新的回文子串结尾相同的回文子串个数(即题中要求的) 
                ch[cur][s[i]-'a']=tot;//最后连边(防止找fail边时找到自己) 
                //求tran函数 (小于等于当前节点长度的一半最长回文后缀)(求法跟fail差不多) 
                if(len[tot]<=2)tran[tot]=fail[tot];
                else{
                    tran[tot]=ch[get_tran(s,tran[cur],i,tot)][s[i]-'a'];
                }
            }
            last=ch[cur][s[i]-'a'];
        }
    }
    PAM

    后缀自动机(待补)

  • 相关阅读:
    Git仓库操作笔记[Git repositories]
    supervisor 使用
    python动态加载(二)——动态加载类
    python动态加载(一)——加载方法
    python连接hdfs常用操作
    python对文件进行并行计算初探
    python加载包顺序和PYTHONPATH
    python实现读取数据库的断点续传
    python实现读取文件的断点续传
    python启动一个新进程
  • 原文地址:https://www.cnblogs.com/passione-123456/p/12075773.html
Copyright © 2011-2022 走看看