4566: [Haoi2016]找相同字符
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 950 Solved: 550
[Submit][Status][Discuss]Description
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaaSample Output
10HINT
Source
[Submit][Status][Discuss]
后缀数组+单调栈可以做,但是后缀数组的超长模板加上单调栈的超多细节,想想就复杂。
对于热爱DP或码力不够的选手,SAM则是最佳选择。
给两个式子自己体会什么意思,很好理解。(tmp表示当前匹配上的长度)
sum[i]=sum[fa[i]]+(l[i]-l[fa[i]])*right[i],ans+=sum[fa[p]]+(tmp-l[fa[p]])*right[p]
radixsort然后拓扑DP即可,模板又打错了。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=400100; 9 char s1[N],s2[N]; 10 int n,m,p,lst=1,np,cnt=1,mx[N],right[N],fa[N],son[N][26],q[N],c[N]; 11 ll ans,sum[N]; 12 13 void ext(int c){ 14 p=lst; lst=np=++cnt; mx[np]=mx[p]+1; right[np]++; 15 while (!son[p][c] && p) son[p][c]=np,p=fa[p]; 16 if (!p) fa[np]=1; 17 else{ 18 int q=son[p][c]; 19 if (mx[q]==mx[p]+1) fa[np]=q; 20 else{ 21 int nq=++cnt; mx[nq]=mx[p]+1; 22 memcpy(son[nq],son[q],sizeof(son[q])); 23 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p]; 24 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 25 } 26 } 27 } 28 29 void radix(){ 30 rep(i,0,n) c[i]=0; 31 rep(i,1,cnt) c[mx[i]]++; 32 rep(i,1,n) c[i]+=c[i-1]; 33 for (int i=cnt; i; i--) q[c[mx[i]]--]=i; 34 for (int i=cnt; i; i--) right[fa[q[i]]]+=right[q[i]]; 35 rep(i,1,cnt) sum[q[i]]=sum[fa[q[i]]]+(mx[q[i]]-mx[fa[q[i]]])*right[q[i]]; 36 } 37 38 void solve(){ 39 int tmp=0; p=1; 40 rep(i,1,m){ 41 int c=s2[i]-'a'; 42 if (son[p][c]) p=son[p][c],tmp++; 43 else{ 44 while (p && !son[p][c]) p=fa[p]; 45 if (!p) p=1,tmp=0; else tmp=mx[p]+1,p=son[p][c]; 46 } 47 ans+=sum[fa[p]]+1ll*(tmp-mx[fa[p]])*right[p]; 48 } 49 } 50 51 int main(){ 52 freopen("bzoj4566.in","r",stdin); 53 freopen("bzoj4566.out","w",stdout); 54 scanf("%s",s1+1); n=strlen(s1+1); 55 scanf("%s",s2+1); m=strlen(s2+1); 56 rep(i,1,n) ext(s1[i]-'a'); 57 radix(); solve(); printf("%lld ",ans); 58 return 0; 59 }