BZOJ4566: [Haoi2016]找相同字符
Description
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
bbaa
Sample Output
10
裸题,用SAM来做的话先建出第一个串的后缀自动机,把第二个串拿上去运行,不完整节点直接计算,完整节点打个懒标记最后扫一遍就行了。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; typedef long long ll; const int maxn=400010; int to[maxn][26],l[maxn],fa[maxn],last=1,cnt=1; int f[maxn],siz[maxn],od[maxn],c[maxn]; void extend(int c) { int p,q,np,nq;p=last;l[last=np=++cnt]=l[p]+1;siz[np]=1; for(;!to[p][c];p=fa[p]) to[p][c]=np; if(!p) fa[np]=1; else { q=to[p][c]; if(l[p]+1==l[q]) fa[np]=q; else { l[nq=++cnt]=l[p]+1; fa[nq]=fa[q];fa[q]=fa[np]=nq; memcpy(to[nq],to[q],sizeof(to[nq])); for(;to[p][c]==q;p=fa[p]) to[p][c]=nq; } } } char s[maxn]; int main() { scanf("%s",s); for(int i=0;s[i];i++) extend(s[i]-'a'); scanf("%s",s); int p=1,len=0;ll ans=0; rep(i,1,cnt) c[l[i]]++; rep(i,1,cnt) c[i]+=c[i-1]; dwn(i,cnt,1) od[c[l[i]]--]=i; dwn(i,cnt,1) siz[fa[od[i]]]+=siz[od[i]]; for(int i=0;s[i];i++) { int c=s[i]-'a'; if(to[p][c]) len++,p=to[p][c]; else { while(p&&!to[p][c]) p=fa[p]; if(p) len=l[p]+1,p=to[p][c]; else p=1,len=0; } ans+=(ll)siz[p]*(len-l[fa[p]]);f[fa[p]]++; } dwn(i,cnt,1) { int x=od[i];f[fa[x]]+=f[x]; ans+=(ll)f[x]*siz[x]*(l[x]-l[fa[x]]); } printf("%lld\n",ans); return 0; }