BZOJ_4566_[Haoi2016]找相同字符_后缀自动机
BZOJ_4566_[Haoi2016]找相同字符_后缀自动机
Description
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
bbaa
Sample Output
10
对两个串建立广义后缀自动机。
设siz[i][0],siz[i][1]分别表示这个串分别在两个串中出现多少次,这个后缀树上DP即可求出。
答案就是(dep[p]-dep[fa[p]])*siz[p][0]*siz[p][1]。
注意插入每个串之前要重置lst=1
代码:
#include <cstdio> #include <string.h> #include <algorithm> using namespace std; #define N 800050 typedef long long ll; int ch[N<<1][26],fa[N<<1],dep[N<<1],siz[N<<1][2],cnt=1,lst=1; int ws[N],a[N]; char w[N],s[N]; void insert(int x) { int p=lst,np,q,nq; if(ch[p][x]) { q=ch[p][x]; if(dep[q]==dep[p]+1) lst=q; else { fa[nq=++cnt]=fa[q]; lst=nq; dep[nq]=dep[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[q]=nq; for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } }else { np=++cnt; lst=np; dep[np]=dep[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np; if(!p) fa[np]=1; else { q=ch[p][x]; if(dep[q]==dep[p]+1) fa[np]=q; else { fa[nq=++cnt]=fa[q]; dep[nq]=dep[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[q]=fa[np]=nq; for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } } void print() { int i,j; printf("test-------------------------------------------\n"); for(i=1;i<=cnt;i++) { printf("p=%d,siz=%d,dep=%d,fa=%d\n",i,siz[i][0],dep[i],fa[i]); for(j=0;j<26;j++) { if(ch[i][j]) { printf("ch(%d)(%c)=%d\n",i,j+'a',ch[i][j]); } } } printf("lst=%d\n",lst); } int main() { scanf("%s%s",w+1,s+1); int lw=strlen(w+1),ls=strlen(s+1); int i; for(i=1;i<=lw;i++) insert(w[i]-'a'),siz[lst][0]++; lst=1; for(i=1;i<=ls;i++) insert(s[i]-'a'),siz[lst][1]++; // print(); for(i=1;i<=cnt;i++) ws[dep[i]]++; for(i=1;i<=cnt;i++) ws[i]+=ws[i-1]; for(i=1;i<=cnt;i++) a[ws[dep[i]]--]=i; for(i=cnt;i;i--) { int p=a[i]; siz[fa[p]][0]+=siz[p][0]; siz[fa[p]][1]+=siz[p][1]; } ll ans=0; for(i=cnt;i;i--) { int p=a[i]; ans+=1ll*(dep[p]-dep[fa[p]])*siz[p][0]*siz[p][1]; } printf("%lld\n",ans); }