题意:给若干字符串,求它们的最长公共子串的长度。
题解:后缀自动机。
对第一个串建立SAM,用后面的串分别匹配。
对于SAM,每个节点新增两个值MIN,MAX;
MIN代表该节点满足所有字符串的最大值,初始化为该字符串建立时的最大值,每次匹配后更新。
MAX代表该节点满足单一字符串时的最大值,匹配完一个字符串后重置为0;
每次匹配完字符串,按照拓扑序从后往前更新(保证父节点在子节点后被更新)
用子节点的MAX更新父节点的MAX(父节点是子节点的后缀,子节点能匹配的长度只会大等于父节点。)
最后求MIN的最大值即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int Maxn=2e5+50;
inline int read()
{
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
int n,m,m1;
char s[Maxn];
struct sam
{
int last,cnt,rt,son[Maxn][26],link[Maxn],len[Maxn],mx[Maxn],mn[Maxn],que[Maxn],c[Maxn];
sam()
{
last=rt=++cnt;len[0]=-1;
for(int i=0;i<=25;i++)son[0][i]=1;
}
inline void extend(int c)
{
int np=++cnt,p=last;last=cnt;
len[np]=mn[np]=len[p]+1;
while(p&&!son[p][c])son[p][c]=np,p=link[p];
if(!p)link[np]=rt;
else
{
int q=son[p][c];
if(len[q]==len[p]+1)link[np]=q;
else
{
int nq=++cnt;len[nq]=mn[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof(int)*26);
link[nq]=link[q];link[q]=link[np]=nq;
while(p&&son[p][c]==q)son[p][c]=nq,p=link[p];
}
}
}
inline void update()
{
m=strlen(s+1);
memset(mx,0,sizeof(mx));
int nowpos=1,nowlen=0;
for(int i=1;i<=m;i++)
{
int c=s[i]-'a';
if(son[nowpos][c])
{
nowpos=son[nowpos][c];nowlen++;
mx[nowpos]=max(mx[nowpos],nowlen);
}
else
{
while(!son[nowpos][c])nowpos=link[nowpos];
nowlen=len[nowpos]+1;nowpos=son[nowpos][c];
mx[nowpos]=max(mx[nowpos],nowlen);
}
}
memset(c,0,sizeof(c));
for(int i=1;i<=cnt;i++)c[len[i]]++;
for(int i=1;i<=m1;i++)c[i]+=c[i-1];
for(int i=cnt;i>=1;i--)que[c[len[i]]--]=i;
for(int i=cnt;i>=1;i--)
{
if(link[que[i]])mx[link[que[i]]]=min(max(mx[link[que[i]]],mx[que[i]]),mn[link[que[i]]]);
mn[que[i]]=min(mn[que[i]],mx[que[i]]);
}
}
}sam;
int main()
{
scanf("%d",&n);scanf("%s",s+1);m1=strlen(s+1);
for(int i=1;i<=m1;i++)sam.extend(s[i]-'a');
while(scanf("%s",s+1)!=EOF)sam.update();
int ans=0;
for(int i=sam.cnt;i>=1;i--)ans=max(ans,sam.mn[i]);
printf("%d\n",ans);
}