【题目链接】
http://www.spoj.com/problems/LCS2/en/
【题意】
求若干个串的最长公共子串。
【思路】
SAM+DP
先拿个串建个SAM,然后用后面的串匹配,每次将所有的匹配长度记录在状态上取min,然后对所有状态取max即答案。
需要更新fa,因为fa[p]一定比p更优,但匹配的时候可能只更新了p而没有更新fa[p],所以还需要递推一边。
注意mn[p]初始化为l[p]
【代码】
#include<cstdio>
#include<cstring>
using namespace std; const int N = *1e5+; char s[N];
int sz,last,root,l[N],ch[N][],fa[N],mn[N],mx[N];
int b[N],cnt[N];
void add(int x) {
int c=s[x]-'a';
int p=last,np=++sz; last=np;
l[np]=mn[np]=x+;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=root;
else {
int q=ch[p][c];
if(l[p]+==l[q]) fa[np]=q;
else {
int nq=++sz; l[nq]=mn[nq]=l[p]+;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[np]=fa[q]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
} int main() {
root=last=++sz;
scanf("%s",s);
int len=strlen(s);
for(int i=;i<len;i++) add(i);
for(int i=;i<=sz;i++) cnt[l[i]]++;
for(int i=;i<=len;i++) cnt[i]+=cnt[i-];
for(int i=;i<=sz;i++) b[cnt[l[i]]--]=i;
while(scanf("%s",s)==) {
int p=root; len=;
for(int i=;s[i];i++) {
int c=s[i]-'a';
if(ch[p][c]) { len++; p=ch[p][c]; }
else {
while(p&&!ch[p][c]) p=fa[p];
if(!p) { len=; p=root; }
else { len=l[p]+; p=ch[p][c]; }
}
if(len>mx[p]) mx[p]=len;
}
for(int i=sz;i;i--) {
p=b[i];
if(mx[p]<mn[p]) mn[p]=mx[p];
if(fa[p] && mx[fa[p]]<mx[p]) mx[fa[p]]=mx[p];
mx[p]=;
}
}
int ans=;
for(int i=;i<=sz;i++)
if(mn[i]>ans) ans=mn[i];
printf("%d",ans);
return ;
}