1. 程式人生 > >4566. [HAOI2016]找相同字符【後綴自動機】

4566. [HAOI2016]找相同字符【後綴自動機】

nbsp con 向上 長度 NPU struct emp int namespace

Description

給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不同當且僅當這兩 個子串中有一個位置不同。

Input

兩行,兩個字符串s1,s2,長度分別為n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母

Output

輸出一個整數表示答案

Sample Input

aabb
bbaa

Sample Output

10 很容易可以想到,我們要對字符串A建立SAM,然後用B串在A上跑
只要能匹配到這個節點,那麽它順著fa指針向上的那一串節點都可以產生貢獻。
每個節點的貢獻是$|R(s)|*max{(min{Max,now}-Min+1),0}$,now為當前匹配長度
實際上當匹配到某個節點s的時候,now一定大於等於fa(s)
所以上面的節點直接用Max更新就可以。需要特判的只是當前的節點
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N (400000+1000)
 5 using namespace std;
 6 
 7 char s[N],t[N];
 8 long long Ans;
 9 
10 struct SAM
11 {
12     int son[N][28],fa[N],step[N],right[N],wt[N],od[N];
13 int p,q,np,nq,last,cnt; 14 SAM(){last=++cnt;} 15 16 void Insert(int x) 17 { 18 p=last; np=last=++cnt; step[np]=step[p]+1; right[np]=1; 19 while (!son[p][x] && p) son[p][x]=np,p=fa[p]; 20 if (!p) fa[np]=1; 21 else 22 { 23
q=son[p][x]; 24 if (step[p]+1==step[q]) fa[np]=q; 25 else 26 { 27 nq=++cnt; step[nq]=step[p]+1; 28 memcpy(son[nq],son[q],sizeof(son[q])); 29 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 30 while (son[p][x]==q) son[p][x]=nq,p=fa[p]; 31 } 32 } 33 } 34 void Init() 35 { 36 int len=strlen(s); 37 for (int i=1; i<=cnt; ++i) wt[step[i]]++; 38 for (int i=1; i<=len; ++i) wt[i]+=wt[i-1]; 39 for (int i=cnt; i>=1; --i) od[wt[step[i]]--]=i; 40 for (int i=cnt; i>=1; --i) right[fa[od[i]]]+=right[od[i]]; 41 } 42 void Solve(char s[]) 43 { 44 int now=1,len=strlen(s),temp=0; 45 for (int i=0; i<len; ++i) 46 { 47 while (now && !son[now][s[i]-a]) 48 now=fa[now],temp=step[now]; 49 if (now==0){now=1; continue;} 50 now=son[now][s[i]-a],temp++; 51 Ans+=1ll*right[now]*(temp-step[fa[now]]); 52 int t=now; 53 while (fa[t]) t=fa[t],Ans+=1ll*right[t]*(step[t]-step[fa[t]]); 54 } 55 } 56 }SAM; 57 58 int main() 59 { 60 scanf("%s%s",s,t); 61 int len=strlen(s); 62 for (int i=0; i<len; ++i) 63 SAM.Insert(s[i]-a); 64 SAM.Init(); 65 SAM.Solve(t); 66 printf("%lld",Ans); 67 }

4566. [HAOI2016]找相同字符【後綴自動機】