bzoj4566 / P3181 [HAOI2016]找相同字符
后缀自动机
(正解应是广义后缀自动机)
并不会广义后缀自动机。
然鹅可以用普通的后缀自动机。
我们先引入一个问题:算出从一个串内取任意两个不重合子串完全相同的方案数。
显然,对于每个点$w$,$tot+=siz[w]*(siz[w]-1)/2*(len[w]-len[fa[w]])$
$siz[w]$表示该点对应子串出现次数
那么答案即为$tot_{a+b}-tot_a-tot_b$
计算$tot_{a+b}$时在$a,b$间插入一个特殊字符即可。(插入$'{'='z'+1$较方便)
attention:数组需要开n*2*2=800000大小!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define N 800005 6 int n; char s1[N],s2[N]; 7 long long ans,tot; 8 struct Sam{ 9 int nxt[N][27],len[N],siz[N],fa[N]; 10 int p,q,last,ed,a[N],c[N]; 11 void clear(){ 12 last=ed=1; 13 memset(fa,0,sizeof(fa)); 14 memset(nxt,0,sizeof(nxt)); 15 memset(len,0,sizeof(len)); 16 memset(siz,0,sizeof(siz)); 17 memset(c,0,sizeof(c)); 18 } 19 void add(int c){ 20 p=last; len[last=++ed]=len[p]+1; siz[ed]=1; 21 for(;p&&!nxt[p][c];p=fa[p]) nxt[p][c]=ed; 22 if(!p){fa[ed]=1; return;} 23 q=nxt[p][c]; 24 if(len[q]==len[p]+1){fa[ed]=q; return;} 25 len[++ed]=len[p]+1; 26 memcpy(nxt[ed],nxt[q],sizeof(nxt[q])); 27 fa[ed]=fa[q]; fa[q]=fa[ed-1]=ed; 28 for(;nxt[p][c]==q;p=fa[p]) nxt[p][c]=ed; 29 }//裸的板子 30 void calc(){ 31 for(int i=1;i<=ed;++i) ++c[len[i]]; 32 for(int i=1;i<=ed;++i) c[i]+=c[i-1]; 33 for(int i=1;i<=ed;++i) a[c[len[i]]--]=i;//对len进行排序代替dfs 34 for(int i=ed;i;--i){ 35 int w=a[i]; siz[fa[w]]+=siz[w]; 36 tot+=1ll*siz[w]*(siz[w]-1)/2*(len[w]-len[fa[w]]);//累计每个点的贡献 37 } 38 } 39 }sam; 40 void solve(char *v,int x){ 41 tot=0; n=strlen(v+1); sam.clear(); 42 for(int i=1;i<=n;++i) sam.add(v[i]-'a'); 43 sam.calc(); ans+=tot*x; 44 } 45 int main(){ 46 scanf("%s",s1+1); scanf("%s",s2+1); 47 solve(s1,-1); solve(s2,-1); 48 strcat(s1+1,"{"); strcat(s1+1,s2+1); 49 solve(s1,1); printf("%lld",ans); 50 return 0; 51 }