字符串板子

 上板子

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

后缀自动机(待补)

原文地址:https://www.cnblogs.com/passione-123456/p/12075773.html