[Poi2000]公共串

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <cstring>
 6 #define maxn 4005
 7 #define maxm 2005
 8 using namespace std;
 9 
10 int n,m,tot,last,root,len,smin[maxn],sum[maxn],tmp[maxn],fa[maxn],son[maxn][26],dist[maxn];
11 char st[maxm];
12 struct Tsegment{
13     int ans[maxn];
14     void prepare(){tot=last=root=1;}
15     int newnode(int x){dist[++tot]=x;return tot;}
16     void add(int x){
17         int p=last,np=newnode(dist[p]+1); last=np;
18         for (;p&&!son[p][x];p=fa[p]) son[p][x]=np;
19         if (p==0) fa[np]=root;
20         else{
21             int q=son[p][x];
22             if (dist[p]+1==dist[q]) fa[np]=q;
23             else{
24                 int nq=newnode(dist[p]+1);
25                 memcpy(son[nq],son[q],sizeof(son[q]));
26                 fa[nq]=fa[q],fa[q]=fa[np]=nq;
27                 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq;
28             }
29         }
30     }
31     void Fsort(){
32         memset(sum,0,sizeof(sum));
33         for (int i=1;i<=tot;i++) ans[i]=dist[i];
34         for (int i=1;i<=tot;i++) sum[dist[i]]++;
35         for (int i=1;i<=tot;i++) sum[i]+=sum[i-1];
36         for (int i=1;i<=tot;i++) tmp[sum[dist[i]]--]=i;
37     }
38     void work(){
39         scanf("%s",st+1),m=strlen(st+1);int x;
40         memset(smin,0,sizeof(smin)),len=0,last=root;
41         for (int i=1;i<=m;i++){
42             x=st[i]-'a';
43             if (son[last][x]) len++,last=son[last][x];
44             else{
45                 for (;last&&!son[last][x];) last=fa[last];
46                 if (last==0) len=0,last=root;
47                 else{
48                     len=dist[last]+1,last=son[last][x];
49                 } 
50             }
51             smin[last]=max(smin[last],len);
52         }
53         for (int i=tot;i>=1;i--){
54             x=tmp[i];
55             ans[x]=min(ans[x],smin[x]);
56             if (fa[x]&&smin[x]) smin[fa[x]]=dist[fa[x]];
57         }
58     }
59 }SAM;
60 
61 int main(){
62     int ans=0;
63     scanf("%d",&n),n--;
64     SAM.prepare();
65     scanf("%s",st+1),m=strlen(st+1);
66     for (int i=1;i<=m;i++) SAM.add(st[i]-'a');
67     SAM.Fsort();
68     for (int i=1;i<=n;i++) SAM.work();
69     for (int i=1;i<=tot;i++) ans=max(ans,SAM.ans[i]);
70     printf("%d\n",ans);
71     return 0;
72 }
View Code

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2946

http://begin.lydsy.com/JudgeOnline/problem.php?id=2797

题目大意:给定n个字符串,n不大于5,每个字符串的长度不大于2000,求这n个字符串的最长公共子串的长度。

做法:首先,这题还是可以用后缀数组+二分答案解决,那后缀自动机怎么解决呢?

首先对其中一个串建立后缀自动机,其他串在上面匹配即可,失配则跳parent树。

对于多串,我们在每个节点上记录一个信息,表示所有匹配的字符串匹配到该节点是最小的长度,每次用该字符串在SAM上每个点的最大匹配长度去更新要维护的信息(最小的长度),原因自己YY一下即可,木桶效应嘛。

特别注意:在后缀自动机上匹配到一个节点时,那么它的所有祖先(通过fa一路可以到达的节点)都能匹配到其最大长度,每次记得通过一次dp得到。

后缀自动机。

 

posted @ 2016-05-31 21:41  oyzx~  阅读(227)  评论(0编辑  收藏  举报