BZOJ 2946: [Poi2000]公共串 (广义后缀自动机在线处理)

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88875959

给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果

可以把广义后缀自动机造出来然后每个前缀的结束点往上暴力跳,直到当前点被当前串覆盖过为止。如果当前串长度为 l l ,自动机大小为 n n ,那么复杂度是 m i n ( l 2 , n ) min(l^2,n)
平均每个字符的复杂度贡献为 m i n ( l 2 , n ) l < = n \frac{min(l^2,n)}l <= \sqrt n (这个证明和在线AC自动机的复杂度证明是一致的)。
那么复杂度上限就是 O ( n l ) O(\sqrt n \sum l)
注意在边插入边修改的情况下复制节点复制的信息要全。
AC Code:

#include<bits/stdc++.h>
#define maxn 20005
#define maxc 26
using namespace std;

int tr[maxn][maxc],fail[maxn],len[maxn];
int tot;
char s[2005];
int vis[maxn],cnt[maxn];
void ins(int p,int c){
	if(tr[p][c]){
		int q=tr[p][c];
		if(len[q]==len[p]+1) return;
		else{
			int cln = ++tot;
			memcpy(tr[cln],tr[q],sizeof tr[q]),cnt[cln]=cnt[q],
			vis[cln]=vis[q],fail[cln]=fail[q],len[cln]=len[p]+1;
			for(;p!=-1&&tr[p][c]==q;p=fail[p]) tr[p][c]=cln;
			fail[q]=cln;
		}
	}
	else{
		int cur=++tot,q;
		len[cur] = len[p] + 1;
		for(;p!=-1 && !tr[p][c];p=fail[p]) tr[p][c]=cur;
		if(p==-1) fail[cur]=0;
		else if(len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
		else{ int cln=++tot;
			memcpy(tr[cln],tr[q],sizeof tr[q]),cnt[cln]=cnt[q],
			vis[cln]=vis[q],fail[cln]=fail[q],len[cln]=len[p]+1;
			for(;p!=-1&&tr[p][c]==q;p=fail[p]) tr[p][c]=cln;
			fail[q]=fail[cur]=cln;
		}
	}
}
int main(){
	int n;
	scanf("%d",&n);
	fail[0]=-1;
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		int m=strlen(s),p=0;
		for(int j=0;j<m;j++){
			ins(p,s[j]-'a');
			p=tr[p][s[j]-'a'];
			for(int k=p;k!=-1&&vis[k]<i;k=fail[k])
				vis[k]=i,cnt[k]++;
		}
	}
	int ans = 0;
	for(int i=1;i<=tot;i++)
		if(cnt[i]==n)
			ans = max(ans , len[i]);
	printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88875959
今日推荐