码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 2946 [Poi2000]公共串

时间:2020-03-28 19:52:58      阅读:70      评论:0      收藏:0      [点我收藏+]

标签:后缀数组   ons   +=   scan   lin   strlen   href   com   后缀   

LINK:公共串

给定n个串 求最长公共子串的长度。

可以广义SAM 求出类似于right集的表示分属某个串的东西可以直接暴力跳 当然这里n只有5 所以可以状压一下用按位或 来做 最后扫一下所有节点就行了。

但我打算使用SA来做 串在一起求SA 经典做法是二分 因为很难找到答案。

但是分析性质 可以发现最长的子串连在一起的串肯定少 我们可以利用尺取法来做。

分析答案的位置 不难发现尺取法刚好可以找到答案。

const int MAXN=20010;
int n,m,cnt,sum,ans,l,r;
int h[MAXN],s[MAXN],vis[MAXN],w[MAXN];
int pos[MAXN],x[MAXN],y[MAXN],c[MAXN],sa[MAXN],rk[MAXN];
char a[MAXN],b[MAXN];
inline void SA()
{
	m=150;
	rep(1,cnt,i)++c[x[i]=a[i]];
	rep(1,m,i)c[i]+=c[i-1];
	fep(cnt,1,i)sa[c[x[i]]--]=i;
	for(int k=1;k<=cnt;k=k<<1)
	{
		int num=0;
		rep(cnt-k+1,cnt,i)y[++num]=i;
		rep(1,cnt,i)if(sa[i]>k)y[++num]=sa[i]-k;
		rep(1,m,i)c[i]=0;
		rep(1,cnt,i)++c[x[i]];
		rep(1,m,i)c[i]+=c[i-1];
		fep(cnt,1,i)sa[c[x[y[i]]]--]=y[i];
		rep(1,cnt,i)y[i]=x[i],x[i]=0;
		x[sa[1]]=num=1;
		rep(2,cnt,i)x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
		if(num==cnt)break;
		m=num;
	}
	//rep(1,cnt,i)printf("%d ",sa[i]);
	rep(1,cnt,i)rk[sa[i]]=i;
}
inline void get_H()
{
	int k=0;
	rep(1,cnt,i)
	{
		if(rk[i]==1)continue;
		if(k)--k;//h[i]>=h[i-1]-1
		int j=sa[rk[i]-1];
		while(a[i+k]==a[j+k])++k;
		h[rk[i]]=k;
	}
}
int main()
{
	freopen("1.in","r",stdin);
	gt(n);
	rep(1,n,i)
	{
		scanf("%s",b+1);
		int len=strlen(b+1);
		rep(1,len,j)a[++cnt]=b[j],pos[cnt]=i;
		a[++cnt]=‘z‘+1;
	}
	a[cnt]=0;--cnt;
	if(n==1){printf("%d\n",cnt);return 0;}
	SA();get_H();
	//rep(1,cnt,i)put(rk[i]);
	int L=1,R=1;l=1;
	while(R<=cnt)
	{
		if(sum<n)
		{
			++R;int x=sa[R],y=sa[R-1];
			if(pos[x])++vis[pos[x]],sum+=(vis[pos[x]]==1);
			if(pos[y])++vis[pos[y]],sum+=(vis[pos[y]]==1);
			while(l<=r&&h[R]<=s[r])--r;
			s[++r]=h[R];w[r]=R;
		}
		while(sum==n)
		{
			ans=max(ans,s[l]);++L;
			int x=sa[L],y=sa[L-1];
			if(pos[x])--vis[pos[x]],sum-=(!vis[pos[x]]);
			if(pos[y])--vis[pos[y]],sum-=(!vis[pos[y]]);
			while(w[l]<=L&&l<=r)++l;
		}
	}
	printf("%d\n",ans);
	return 0;
}

坑点:最后一个位置没用 但是必要的是清0 n可能等于1 后缀数组可能手残打错 单调队列两个条件都要加上。

bzoj 2946 [Poi2000]公共串

标签:后缀数组   ons   +=   scan   lin   strlen   href   com   后缀   

原文地址:https://www.cnblogs.com/chdy/p/12588833.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!