• [HAOI2016]找相同字符 广义后缀自动机_统计出现次数


    题目描述:
    给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。

    输入输出格式
    输入格式:
    两行,两个字符串 s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

    输出格式:
    输出一个整数表示答案

    题解:
    对 $2$ 个字符串建立一个广义后缀自动机.
    实际上,广义后缀自动机就是对多个字符串用一个自动机加以维护.
    每加入完毕一个字符串时,将 $last$ 设为 $1$.
    插入字符时,若 $ch[last][c]==0$,则与普通的插入无区别.
    若 $ch[last][c]!=0$ ,我们就将这个情况考虑成普通插入中 $last$ 的祖先中有 $ch[q][c]!=0$ 的情况即可.
    对每一种字符串都维护一个 $cnt$ 数组即可.

    上述讲的是广义后缀自动机的建立.
    在后缀自动机上,由于每个节点的 $right$ 的区间刚好是 $[right[f[p]],right[p]]$,点和点之间的计算时互不矛盾的.
    每个点的贡献为: $(dis[p]-dis[f[p]])*cnt[p][0]*cnt[p][1]$.

    Code:

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define setIO(s) freopen(s".in","r",stdin)
    #define maxn 1000000
    #define N 30
    #define ll long long
    using namespace std;
    char str[maxn];
    int last=1,tot=1,n,m; 
    int ch[maxn][N],cnt[maxn][2],f[maxn],dis[maxn],rk[maxn]; 
    long long C[maxn],ans; 
    void ins(int c){
        int np=++tot,p=last; last=np; 
        if(ch[p][c]){
            int q=ch[p][c];
            if(dis[q]==dis[p]+1) last=q;
            else {
                int nq=++tot; last=nq;
                f[nq]=f[q],dis[nq]=dis[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                f[q]=nq;
                while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
            }
        }
        else{
            dis[np]=dis[p]+1; 
            while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
            if(!p) f[np]=1;
            else{
                int q=ch[p][c],nq; 
                if(dis[q]==dis[p]+1) f[np]=q;
                else{
                    nq=++tot;
                    dis[nq]=dis[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    f[nq]=f[q],f[q]=f[np]=nq;
                    while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
                }
            }
        }
    }
    int main(){
        //setIO("input");
        scanf("%s",str),n=strlen(str);
        for(int i=0;i<n;++i) ins(str[i]-'a'),cnt[last][0]=1; 
        scanf("%s",str),m=strlen(str),last=1;
        for(int i=0;i<m;++i) ins(str[i]-'a'),cnt[last][1]=1; 
        for(int i=1;i<=tot;++i) ++C[dis[i]];
        for(int i=1;i<=tot;++i) C[i]+=C[i-1];
        for(int i=1;i<=tot;++i) rk[C[dis[i]]--]=i;
        for(int i=tot;i>=1;--i) {
            int p=rk[i];
            cnt[f[p]][0]+=cnt[p][0],cnt[f[p]][1]+=cnt[p][1];
            ans+=(ll)(dis[p]-dis[f[p]])*cnt[p][0]*cnt[p][1]; 
        } 
        printf("%lld",ans); 
        return 0; 
    } 
    

      

  • 相关阅读:
    [转]
    Linux
    [转]
    [转]
    Linux 高级网络编程
    [转]
    [译]- 6-1 排列窗体上的控件(Laying Out Widgets on a Form)
    [转]
    [转]
    the thread has exited with code -1073741819
  • 原文地址:https://www.cnblogs.com/guangheli/p/10293808.html
Copyright © 2020-2023  润新知