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

130 篇文章 1 订阅
22 篇文章 0 订阅

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

可以把广义后缀自动机造出来然后每个前缀的结束点往上暴力跳,直到当前点被当前串覆盖过为止。如果当前串长度为 l l l,自动机大小为 n n n,那么复杂度是 m i n ( l 2 , n ) min(l^2,n) min(l2,n)
平均每个字符的复杂度贡献为 m i n ( l 2 , n ) l &lt; = n \frac{min(l^2,n)}l &lt;= \sqrt n lmin(l2,n)<=n (这个证明和在线AC自动机的复杂度证明是一致的)。
那么复杂度上限就是 O ( n ∑ l ) O(\sqrt n \sum l) O(n 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值