Scx117
只一眼,便辽阔了时间。

题意:给你两个字符串,问其中各取一个子串,有多少对相同?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 }
View Code

 

题解:后缀自动机

参考了mjy0724的做法,把两个串的后缀自动机建在一起,对于每一个节点分别统计在A/B串中的出现次数,统计sz1[i]*sz2[i]*(l[i]-l[fa[i]])。

也可以一个串建Sam,另一个串在Sam上匹配,每次统计以新加入字符为后缀的字符串的匹配情况。匹配到的Sam节点以上的节点都是该节点的后缀,都要统计,做个树上前缀和。注意一下该点内部的匹配,l=min(l,dep[p])+1。

也可以用后缀数组做,加一个分隔符,利用height数组从小到大加入。

posted on 2018-06-26 10:21  Scx117  阅读(187)  评论(0编辑  收藏  举报