解题思路:多个串的最长公共子串后缀自动机,把一个串建成树,因为根节点到每个节点都是他的子串,那么去取每个节点也就是该串的每个子串与其他串的相同长度,用mn[i]表示该节点表示的子串和其他串相同的最小长度,然后最后取从1-tot节点中取max就可以了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
char str[mx],Ts[mx];
struct state
{
int fa,len;//fa为包含该转态的最相近节点,len是从根节点走到该转态的最长长度
int id[26];
}Tr[2*mx];
int tot,last,ans,cnt[2*mx],v[2*mx],pos[2*mx];
int mn[mx<<1];
//后缀自动机定义,从根节点出发到达任意一个节点形成的串都是唯一的,且一定是母串的子串
void suf_auto(int ch)
{
int np = ++tot,p = last;
//其实last就是原来的一条直串扩展到的前一个位置
//比如abccbdab这个串,自动机上肯定存在最长的一个路径就是这个串,那么last就是一直在这上面的
Tr[np].len = Tr[last].len + 1;
last = np;
//for(;p&&!Tr[p].id[ch];p=Tr[p].fa) Tr[p].id[ch] = np;
while(p&&!Tr[p].id[ch]) Tr[p].id[ch] = np, p = Tr[p].fa;
//找到包含p转态的节点,那么它也可以转移到新转态np
if(!p) Tr[np].fa = 1;//如果都没有包含p状态的节点有可以扩展出ch这个转态,那么新状态被根节点包含
else{
int q = Tr[p].id[ch];
//如果if成立,不存在另一个点t使得Tr[t].len + 1==Tr[q].len证明也很简单
if(Tr[p].len+1==Tr[q].len) Tr[np].fa = q;//说明q状态可以包含新状态np
else{
int nq = ++tot;
Tr[nq] = Tr[q];//分裂出q转态,就是分裂出if判断那个节点
Tr[nq].len = Tr[p].len + 1;
Tr[q].fa = Tr[np].fa = nq;//分裂出的nq肯定可以包含原来的点q和np
while(p&&Tr[p].id[ch]==q) Tr[p].id[ch] = nq,p = Tr[p].fa;
//将后面包含p的点的儿子是q的也变成分裂的那个,因为他们就是跟着p走的,分裂出来的nq自然是p直接扩展状态
}
}
}
void init()//初始化根节点为1号
{
memset(mn,inf,sizeof(mn));
last = tot = 1;
Tr[1].len = Tr[1].fa = 0;
//memset(Tr[1].id,0,sizeof(Tr[1].id));
//这里注意多个数据id数组要初始化
}
void find(int len)
{
memset(cnt,0,sizeof(cnt));
int ret = 0,p = 1;
for(int i=0;i<len;i++){
int ch = Ts[i] - 'a';
if(Tr[p].id[ch]) ret++,p = Tr[p].id[ch];
else{
while(p&&!Tr[p].id[ch]) p = Tr[p].fa;
if(p) ret = Tr[p].len + 1,p = Tr[p].id[ch];
else p = 1,ret = 0;
}
cnt[p] = max(ret,cnt[p]);
}
for(int i=tot;i>=1;i--){
int po = pos[i];
mn[po] = min(mn[po],cnt[po]);
if(cnt[po]) cnt[Tr[po].fa] = Tr[Tr[po].fa].len;//说明pre节点任意状态都可以匹配
}
}
int main()
{
init();
scanf("%s",str);
int len = strlen(str);
for(int i=0;i<len;i++) suf_auto(str[i]-'a');
for(int i=1;i<=tot;i++) v[Tr[i].len]++;//这里要拓扑排序一下,因为有可能新节点是旧的pre,旧的也可能是新的pre
for(int i=1;i<=tot;i++) v[i] += v[i-1];//根据节点最长子串长度排,这样就会先考虑后继然后再去更新前驱
for(int i=1;i<=tot;i++) pos[v[Tr[i].len]--] = i;
while(~scanf("%s",Ts)){
len = strlen(Ts);
find(len);
}
for(int i=1;i<=tot;i++) ans = max(mn[i],ans);
printf("%d\n",ans);
return 0;
}