题意
给出几个字符串,求他们的最长公共子串
题解
其实我们只需要能够对于两个串求出它们的最长公共子串,这道题就差不多解决了
我们对第一个串构建后缀自动机,之后的跟它匹配就可以了
对于每个结点,每次匹配时维护当前匹配长度的最大值,再对全局维护一个最小值(因为要公共),然后最终答案就是所有点最小值的最大值
但是需要注意的是,当我们匹配了某个节点后,我们要更新它的fa的最大值,因为这个点能匹配就意味着fa一定能被完全匹配掉。当然我们没有必要在匹配的过程中做,我们在匹配完成后拓扑序更新即可。(此处拓扑排序直接用的基数排序)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000000+5,T=1000000+5;
const int INF=0x3f3f3f3f;
typedef long long ll;
char s[N];
struct node{
node *fail;
node *ch[26];
int len;
}a[T],*last,*tcnt,*root;
void Init(){
last=tcnt=root=&a[0];
}
void Debug(){
for(int i=0;root+i<=tcnt;i++){
printf("%d:\n",i);
for(int j=0;j<26;j++)
if((root+i)->ch[j])
printf("->%d\n",(root+i)->ch[j]-root);
if(i)
printf("fa: %d\n",(root+i)->fail-root);
printf("len:%d\n",(root+i)->len);
}
}
void Insert(char *s){
int n=strlen(s);
last=root;
for(int i=0;i<n;i++){
int c=s[i]-'a';
node *p=last,*np=++tcnt,*q,*nq;
np->len=p->len+1;
last=np;
while(p&&!p->ch[c])
p->ch[c]=np,p=p->fail;
if(!p)
np->fail=root;
else{
q=p->ch[c];
if(q->len==p->len+1)
np->fail=q;
else{
nq=++tcnt;
*nq=*q;
nq->len=p->len+1;
np->fail=nq;
q->fail=nq;
while(p&&p->ch[c]==q)
p->ch[c]=nq,p=p->fail;
}
}
}
}
int mn[N];
int b[N];
int z[N];
int len[N];
void Sort(int m){
for(int i=1;root+i<=tcnt;i++)
b[(root+i)->len]++;
for(int i=1;i<=m;i++)
b[i]+=b[i-1];
for(int i=1;root+i<=tcnt;i++)
z[b[(root+i)->len]--]=i;
}
void Match(char *s){
memset(len,0,sizeof len);
int m=strlen(s);
node *p=root;
int t=0;
for(int i=0;i<m;i++){
int c=s[i]-'a';
if(p->ch[c])
p=p->ch[c],t++;
else{
while(p&&!p->ch[c])
p=p->fail;
if(!p)
p=root,t=0;
else
t=p->len+1,p=p->ch[c];
}
len[p-root]=max(len[p-root],t);
}
for(int i=tcnt-root;i>0;i--){
int p=z[i];
node *q=root+p;
mn[p]=min(mn[p],len[p]);
if(len[p]){
len[q->fail-root]=q->fail->len;
}
}
}
int main()
{
Init();
memset(mn,0x3f,sizeof mn);
int n;
scanf("%d",&n);
scanf("%s",s);
int m=strlen(s);
Insert(s);
//Debug();
Sort(m);
for(int i=2;i<=n;i++){
scanf("%s",s);
Match(s);
}
int ans=0;
for(int i=1;root+i<=tcnt;i++)
ans=max(ans,mn[i]);
printf("%d\n",ans);
}