SP1812 LCS2 - Longest Common Substring II
题面传送门
先来考虑两个字符串的最长公共子串怎么求。
考虑SAM有一个性质:源点到任意点的任意路径都是原串的一个子串。
这个性质就可以搞了。
首先对于第一个串建立AC自动机,然后让第二个串在第一个串上跑匹配。
这样就可以计算答案了。
但是对于多个串怎么办呢?
可以在每个节点上都对于每个串计算答案并取\(\min\)
注意一个串匹配到节点\(x\)则\(x\)在parent树上的祖先都是可以匹配的。
所以每个串做完要统一往上推。
code:
#include<cstdio>
#include<cstring>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define l(x) x<<1
#define r(x) x<<1|1
#define N 500039
#define ll long long
#define ui unsigned int
using namespace std;
int n,m,k,x,y,z,ans,tot,pus;char a[13][N];
struct SAM{
int fa[N],son[N][26],len[N],cur,pus,p,now,last=1,cnt=1,g[N],h[N],f[N],b[N],m,i;
I void insert(int x){
p=last;now=last=++cnt;len[now]=len[p]+1;while(p&&!son[p][x]) son[p][x]=now,p=fa[p];
if(!p) return (void)(fa[now]=1);
cur=son[p][x];if(len[cur]==len[p]+1) fa[now]=cur;
else{
fa[pus=++cnt]=fa[cur];memcpy(son[pus],son[cur],sizeof(son[pus]));len[pus]=len[p]+1;fa[now]=fa[cur]=pus;
while(p&&son[p][x]==cur) son[p][x]=pus,p=fa[p];
}
}
I void sort(){
for(i=1;i<=cnt;i++)f[len[i]]++;
for(i=1;i<=cnt;i++) f[i]+=f[i-1];
for(i=cnt;i;i--) b[f[len[i]]--]=i;
}
I void solve(char *a){
for(i=1;i<=cnt;i++)h[i]=0;
for(m=strlen(a+1),p=1,tot=0,i=1;i<=m;i++){
now=a[i]-'a';if(son[p][now]) p=son[p][now],tot++;
else{
while(p&&!son[p][now]) p=fa[p];tot=len[p]+1;
p=son[p][now];!p&&(p=1,tot=0);
}
h[p]=max(h[p],tot);
}
for(i=cnt;i;i--) now=b[i],h[fa[now]]=max(h[fa[now]],h[now]),g[now]=min(g[now],h[now]);
}
}s;
int main(){
freopen("1.in","r",stdin);
register int i;for(n=1;~scanf("%s",a[n]+1);n++);m=strlen(a[1]+1);for(i=1;i<=m;i++) s.insert(a[1][i]-'a');
for(i=1;i<=s.cnt;i++)s.g[i]=s.len[i];s.sort();for(i=2;i<n;i++)s.solve(a[i]);for(i=1;i<=s.cnt;i++)ans=max(ans,s.g[i]);printf("%d\n",ans);
}