http://www.spoj.com/problems/LCS2/
题意:
求10个串的LCS
1、用第一个串建立后缀自动机
2、len[s] 表示状态s 所能代表的字符串的最大长度
mx[s] 表示状态s 在 当前匹配的串的最长匹配后缀长度
ans[s] 表示状态s 在所有串的最长匹配后缀长度
3、用第2——第10个串在后缀自动机上跑,每次mx[s]=max(mx[s],当前匹配长度)
每一个串跑完之后,更新 ans[s]=min(ans[s],mx[s])
4、每次匹配完一个字符串的时候,要 从后缀自动机 parent 树 上的叶子节点 向根更新,
因为后缀自动机的parent树上,min[s]=max(fa[s])+1,所以子节点能匹配的长度 比 父节点的max要长。父节点是子节点的后缀,父节点可以匹配子节点的后max(fa[s])位,但是在那串在后缀自动机上跑的时候,不能保证经过 s 的 时候 也经过了s到根的链。所以只要子节点s 有匹配长度,父节点的mx[fa[s]]即可修改为len[fa[s]]即max(fa[s])
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100002 char s[N]; int tot=1,len[N<<1],ch[N<<1][26],fa[N<<1]; int last,p,q,np,nq; int c[N],sa[N<<1]; int mx[N<<1],ans[N<<1]; void extend(int c) { len[np=++tot]=len[last]+1; ans[tot]=len[tot]; for(p=last;p && !ch[p][c];p=fa[p]) ch[p][c]=np; if(!p) fa[np]=1; else { q=ch[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { nq=++tot; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q]; fa[q]=fa[np]=nq; len[nq]=len[p]+1; for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } last=np; } void build() { scanf("%s",s+1); int L=strlen(s+1); for(int i=1;i<=L;++i) extend(s[i]-‘a‘); for(int i=1;i<=tot;++i) c[len[i]]++; for(int i=1;i<=L;++i) c[i]+=c[i-1]; for(int i=tot;i;--i) sa[c[len[i]]--]=i; } void solve() { int L,c,now,now_len; int x; while(scanf("%s",s+1)!=EOF) { L=strlen(s+1); now=1; for(int i=1;i<=L;++i) { c=s[i]-‘a‘; while(now && !ch[now][c]) { now=fa[now]; now_len=len[now]; } if(!now) { now_len=0; now=1; } else if(ch[now][c]) { now_len++; now=ch[now][c]; } mx[now]=max(mx[now],now_len); } for(int i=tot;i;--i) { x=sa[i]; ans[x]=min(ans[x],mx[x]); if(fa[p] && mx[x]) mx[fa[p]]=min(mx[fa[p]],mx[x]); mx[x]=0; } } int out=0; for(int i=1;i<=tot;++i) out=max(out,ans[i]); printf("%d",out); } int main() { build(); solve(); return 0; }