题意:给你两个字符串,问其中各取一个子串,有多少对相同?n<=20W。
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1000005; 5 ll ans; 6 int p,np,cnt,fa[N],son[N][26],tt[N],sz1[N],sz2[N],l[N],sl,tl,Cnt[N]; 7 char a[N],b[N]; 8 void sam(int c) 9 { 10 p=np; np=++cnt;l[np]=l[p]+1; 11 if (son[p][c]&&l[son[p][c]]==l[p]+1) {cnt--,np=son[p][c];return;} 12 for (;p&&!son[p][c];p=fa[p]) son[p][c]=np; 13 if (!p) fa[np]=1; 14 else { 15 int q=son[p][c]; 16 if (l[q]==l[p]+1) fa[np]=q; 17 else { 18 int nq=++cnt;l[nq]=l[p]+1; 19 memcpy(son[nq],son[q],sizeof(son[q])); 20 fa[nq]=fa[q];fa[q]=fa[np]=nq; 21 for (;p&&son[p][c]==q;p=fa[p]) son[p][c]=nq; 22 } 23 } 24 } 25 int main() 26 { 27 scanf("%s%s",a+1,b+1); 28 sl=strlen(a+1);tl=strlen(b+1); 29 cnt=np=1; 30 for (int i=1;i<=sl;i++) sam(a[i]-'a'),sz1[np]++; 31 np=1; 32 for (int i=1;i<=tl;i++) sam(b[i]-'a'),sz2[np]++; 33 for (int i=1;i<=cnt;i++) Cnt[l[i]]++; 34 for (int i=1;i<=cnt;i++) Cnt[i]+=Cnt[i-1]; 35 for (int i=1;i<=cnt;i++) tt[Cnt[l[i]]--]=i; 36 for (int i=cnt;i>=1;i--) 37 { 38 int x=tt[i]; 39 sz1[fa[x]]+=sz1[x];sz2[fa[x]]+=sz2[x]; 40 } 41 for (int i=1;i<=cnt;i++) ans+=(ll)sz1[i]*sz2[i]*(l[i]-l[fa[i]]); 42 printf("%lld\n",ans); 43 return 0; 44 }
题解:后缀自动机
参考了mjy0724的做法,把两个串的后缀自动机建在一起,对于每一个节点分别统计在A/B串中的出现次数,统计sz1[i]*sz2[i]*(l[i]-l[fa[i]])。
也可以一个串建Sam,另一个串在Sam上匹配,每次统计以新加入字符为后缀的字符串的匹配情况。匹配到的Sam节点以上的节点都是该节点的后缀,都要统计,做个树上前缀和。注意一下该点内部的匹配,l=min(l,dep[p])+1。
也可以用后缀数组做,加一个分隔符,利用height数组从小到大加入。