码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-01-20 00:58:11      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:long   cpp   input   插入   ++i   ret   can   for   表示   

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

输入输出格式
输入格式:
两行,两个字符串 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; 
} 

  

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

标签:long   cpp   input   插入   ++i   ret   can   for   表示   

原文地址:https://www.cnblogs.com/guangheli/p/10293808.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!