1. 程式人生 > >[BZOJ2946][Poi2000]公共串 後綴自動機

[BZOJ2946][Poi2000]公共串 後綴自動機

文件 include poi using string 一次 一行 ont element

2946: [Poi2000]公共串

Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 1367 Solved: 612
[Submit][Status][Discuss]

Description

給出幾個由小寫字母構成的單詞,求它們最長的公共子串的長度。 任務: l 讀入單詞 l 計算最長公共子串的長度 l 輸出結果

Input

文件的第一行是整數 n,1<=n<=5,表示單詞的數量。接下來n行每行一個單詞,只由小寫字母組成,單詞的長度至少為1,最大為2000。

Output

僅一行,一個整數,最長公共子串的長度。

Sample Input

3
abcb
bca
acbc

Sample Output

HINT

Source

第一次寫後綴自動機,感覺非常神奇。

對第一個串建立後綴自動機,多串進行匹配。

每次匹配的時候對於後綴自動機中的每個節點維護ans,ls,表示的是到達該節點所能匹配上的最大後綴長度(每個節點都可能是多個節點的兒子,所以從根到該點的路徑長度可能是不同的)。

我們還需要按照step從大到小,用每個節點取更新他的link節點,因為如果匹配到一個狀態,那麽實際上他link鏈上的所有狀態都匹配了,for(int i=cnt;i;i--) ls[link[pos[i]]]=max(ls[pos[i]],ls[link[pos[i]]]);

然後對於每個串匹配後得到的每個位置的ls數組取min得到數組ans,ans的最大值極為答案。

技術分享圖片
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define maxn 4005
 8 using namespace std;
 9 char ch[maxn];
10 struct data {
11     int
son[maxn][30],step[maxn],link[maxn],last,cnt; 12 int ans[maxn],v[maxn],pos[maxn],ls[maxn]; 13 data() {last=cnt=1;} 14 void extend(int c) { 15 int p=last,np=last=++cnt;step[np]=step[p]+1; 16 while(!son[p][c]&&p) son[p][c]=np,p=link[p]; 17 if(!p) link[np]=1; 18 else { 19 int q=son[p][c]; 20 if(step[q]==step[p]+1) link[np]=q; 21 else { 22 int nq=++cnt; 23 step[nq]=step[p]+1; 24 memcpy(son[nq],son[q],sizeof(son[q])); 25 link[nq]=link[q]; 26 link[np]=link[q]=nq; 27 while(son[p][c]==q) son[p][c]=nq,p=link[p]; 28 } 29 } 30 } 31 void pre() { 32 for(int i=1;i<=cnt;i++) {ans[i]=step[i];v[step[i]]++;} 33 for(int i=1;i<=cnt;i++) v[i]+=v[i-1]; 34 for(int i=cnt;i;i--) pos[v[step[i]]--]=i; 35 } 36 void solve() { 37 memset(ls,0,sizeof(ls)); 38 scanf("%s",ch+1); 39 int l=strlen(ch+1),p=1,tmp=0; 40 for(int i=1;i<=l;i++) { 41 int c=ch[i]-a; 42 while(!son[p][c]&&p) p=link[p]; 43 if(!p) p=1,tmp=0; 44 else tmp=min(tmp,step[p])+1,p=son[p][c]; 45 ls[p]=max(ls[p],tmp); 46 } 47 for(int i=cnt;i;i--) ls[link[pos[i]]]=max(ls[pos[i]],ls[link[pos[i]]]); 48 for(int i=1;i<=cnt;i++) ans[i]=min(ans[i],ls[i]); 49 } 50 void getans() { 51 int an=0; 52 for(int i=1;i<=cnt;i++) an=max(an,ans[i]); 53 printf("%d\n",an); 54 } 55 }sam; 56 int main() { 57 int n; 58 scanf("%d",&n); 59 scanf("%s",ch+1); 60 int l=strlen(ch+1); 61 for(int i=1;i<=l;i++) sam.extend(ch[i]-a); 62 sam.pre(); 63 for(int i=2;i<=n;i++) sam.solve(); 64 sam.getans(); 65 }
View Code

[BZOJ2946][Poi2000]公共串 後綴自動機