题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=4566
题解:
广义后缀自动机
对两个串同时建立一个广义后缀自动机。
同时统计出每个状态对两个串分别的right集合大小right[*][0/1]
那么答案就是$$ANS=\sum_s right[s][0]×right[s][1]×(maxs[s]-maxs[parent[s]])$$
代码:
#include<bits/stdc++.h> #define MAXN 200005 #define ll long long using namespace std; struct SAM{ int size; int maxs[MAXN*6],trans[MAXN*6][26],parent[MAXN*6],right[MAXN*6][2]; SAM(){ memset(trans[0],0,sizeof(trans[0])); size=0; Newnode(0,0); } int Newnode(int a,int b){ ++size; maxs[size]=a; memcpy(trans[size],trans[b],sizeof(trans[b])); return size; } int Extend(int last,int x){ static int p,np,q,nq; p=last; if(trans[p][x]&&maxs[p]+1==maxs[trans[p][x]]) return trans[p][x]; np=Newnode(maxs[p]+1,0); for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np; if(!p) parent[np]=1; else{ q=trans[p][x]; if(maxs[p]+1!=maxs[q]){ nq=Newnode(maxs[p]+1,q); parent[nq]=parent[q]; parent[q]=parent[np]=nq; for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq; } else parent[np]=q; } return np; } void Build(char *S){ static int last; last=1; for(int i=0;S[i];i++) last=Extend(last,S[i]-‘a‘); } ll Count(char *S,char *T,ll ans=0){ static int p,len,tmp[MAXN],order[MAXN*6]; len=max(strlen(S),strlen(T)); p=1; for(int i=0;S[i];i++) p=trans[p][S[i]-‘a‘],right[p][0]++; p=1; for(int i=0;T[i];i++) p=trans[p][T[i]-‘a‘],right[p][1]++; for(int i=1;i<=size;i++) tmp[maxs[i]]++; for(int i=1;i<=len;i++) tmp[i]+=tmp[i-1]; for(int i=1;i<=size;i++) order[tmp[maxs[i]]--]=i; //不同于单串的后缀自动机,因为存在父子关系但是maxs相同的状态,但是可以确定的是这种情况下,父亲标号一定大于儿子标号 for(int i=size;i;i--){ p=order[i]; right[parent[p]][0]+=right[p][0]; right[parent[p]][1]+=right[p][1]; } for(int i=1;i<=size;i++) ans+=1ll*(maxs[i]-maxs[parent[i]])*right[i][0]*right[i][1]; return ans; } }SUF; int main(){ static char S[MAXN],T[MAXN]; scanf("%s%s",S,T); SUF.Build(S); SUF.Build(T); printf("%lld\n",SUF.Count(S,T)); return 0; }