[BZOJ2946][Poi2000]公共串 后缀自动机
2946: [Poi2000]公共串
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 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
abcb
bca
acbc
Sample Output
2
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 }
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~