题意
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
1<=n<=5,表示单词的数量,单词只由小写字母组成,单词的长度至少为1,最大为2000。
分析
先对第一个字符串构建sam,然后对于其余每个串,记录每个位置在后缀自动机上匹配到的最大长度,然后有一个特别关键的转移就是每个儿子节点的mx传给其parents,再在所有位置里面找一个最大的即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 4005
using namespace std;
int n,mx[N],ans[N],fa[N],l[N],ch[N][30],last,cnt,b[N],c[N],m;
char s[N];
void ins(int x)
{
int p,q,np,nq;
p=last;last=np=++cnt;l[np]=l[p]+1;
for (;!ch[p][x]&&p;p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (l[q]==l[p]+1) fa[np]=q;
else
{
nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void solve()
{
memset(mx,0,sizeof(mx));
int now=1,len=0;
for (int i=1;i<=n;i++)
{
s[i]-='a';
for (;!ch[now][s[i]]&&now;now=fa[now]);
if (!now) now=1,len=0;
else len=min(len,l[now])+1,now=ch[now][s[i]];
mx[now]=max(mx[now],len);
}
for (int i=cnt;i>=1;i--) mx[fa[c[i]]]=max(mx[fa[c[i]]],mx[c[i]]);
for (int i=1;i<=cnt;i++) ans[i]=min(ans[i],mx[i]);
}
void prework()
{
for (int i=1;i<=cnt;i++) b[l[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=1;i<=cnt;i++) c[b[l[i]]--]=i;
for (int i=1;i<=cnt;i++) ans[i]=l[i];
}
int main()
{
scanf("%d",&m);
scanf("%s",s+1);
n=strlen(s+1);
last=cnt=1;
for (int i=1;i<=n;i++) ins(s[i]-'a');
prework();
for (int i=1;i<m;i++)
{
scanf("%s",s+1);
n=strlen(s+1);
solve();
}
int w=0;
for (int i=1;i<=cnt;i++) w=max(w,ans[i]);
printf("%d",w);
return 0;
}