[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 }
题目链接: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得到。
后缀自动机。