HAOI2016找相同字符
bzoj4566 / loj2064
题目
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。 L<=200000
做法一:后缀自动机
注意:
- 当前匹配到的长度不一定是当前节点的最长长度 所以:在53、57行统计有关当前节点答案的地方,应用 l[f[p]]+1 或是 len-l[f[l]]
- 注意拓扑排序的遍历顺序皆为从小到大
1 #include<cstring> 2 #include<cstdio> 3 #include<iostream> 4 using namespace std; 5 const int N=600001; 6 int lst=1,cnt=1,sz[N],l[N],f[N],c[N][27]; 7 inline void ins(int x){ 8 int p=lst,np=++cnt;lst=np; 9 l[np]=l[p]+1;sz[np]=1; 10 for(;p&&!c[p][x];p=f[p])c[p][x]=np; 11 if(!p)f[np]=1; 12 else{ 13 int q=c[p][x]; 14 if(l[q]==l[p]+1)f[np]=q; 15 else{ 16 int nq=++cnt; 17 f[nq]=f[q]; 18 memcpy(c[nq],c[q],sizeof(c[q])); 19 l[nq]=l[p]+1; 20 f[q]=f[np]=nq; 21 for(;p&&c[p][x]==q;p=f[p])c[p][x]=nq; 22 } 23 } 24 } 25 int n1,n2; 26 char s1[N>>1],s2[N>>1]; 27 int tmptmp[N],tp[N],sm[N]; 28 inline void TP(){ 29 for(int i=1;i<=cnt;++i)++tmptmp[l[i]]; 30 for(int i=1;i<=n1;++i)tmptmp[i]+=tmptmp[i-1]; 31 for(int i=1;i<=cnt;++i)tp[tmptmp[l[i]]--]=i; 32 } 33 long long ans=0; 34 int main(){ 35 scanf("%s%s",s1,s2);n1=strlen(s1);n2=strlen(s2); 36 for(int i=0;i<n1;++i){ 37 ins(s1[i]-'a'); 38 } 39 TP(); 40 for(int i=cnt;i>=1;--i)sz[f[tp[i]]]+=sz[tp[i]]; 41 for(int i=2;i<=cnt;++i){ 42 sm[tp[i]]=sm[f[tp[i]]]+ sz[tp[i]]*(l[tp[i]]-l[f[tp[i]]]); 43 }// for(int i=1;i<=cnt;++i)printf("sm[%d]=%d sz%d f%d tp%d a%d b%d\n",i,l[i],sz[i],f[i],tp[i],c[i][0],c[i][1]); 44 int p=1,len=0,x; 45 for(int i=0;i<n2;++i){//puts("*"); 46 x=s2[i]-'a'; 47 if(c[p][x]){ 48 ++len;p=c[p][x]; 49 }else{ 50 for(;p&&!c[p][x];p=f[p]); 51 if(!p)len=0,p=1; 52 else{ 53 len=l[p]+1; 54 p=c[p][x]; 55 } 56 } 57 ans+=(long long)(sm[f[p]]+sz[p]*(len-l[f[p]])); 58 } 59 cout<<ans; 60 return 0; 61 }