bzoj 2946: [Poi2000]公共串【SAM】

对第一个串建SAM,把剩下的串在上面跑,每次跑一个串的时候在SAM的端点上记录匹配到这的最大长度,然后对这些串跑的结果取min,然后从这些节点的min中取max就是答案
注意在一个点更新后它的祖先也会被更新,需要最后按拓扑序向上更新一边
其实二分+hash就行

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=10005;
int n,m,ch[N][26],fa[N],tot=1,cur=1,la,dis[N],sa[N],wsu[N],l[N],mn[N],ans;
char s[N];
void ins(int c,int id)
{
	la=cur,dis[cur=++tot]=id;
	int p=la;
	for(;p&&!ch[p][c];p=fa[p])
		ch[p][c]=cur;
	if(!p)
		fa[cur]=1;
	else
	{
		int q=ch[p][c];
		if(dis[q]==dis[p]+1)
			fa[cur]=q;
		else
		{
			int nq=++tot;
			dis[nq]=dis[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			fa[nq]=fa[q];
			fa[q]=fa[cur]=nq;
			for(;ch[p][c]==q;p=fa[p])
				ch[p][c]=nq;
		}
	}
}
void wk()
{
	scanf("%s",s+1);
	memset(l,0,sizeof(l));
	int n=strlen(s+1),nw=1;
	for(int i=1,len=1;i<=n;i++)
	{
		while(nw&&!ch[nw][s[i]-'a'])
			nw=fa[nw];
		if(!nw)
			nw=1,len=0;
		else
			len=min(len,dis[nw])+1,nw=ch[nw][s[i]-'a'];//cerr<<len<<endl;
		l[nw]=max(l[nw],len);
	}
	for(int i=tot;i>=2;i--)
		l[fa[sa[i]]]=max(l[fa[sa[i]]],min(dis[fa[sa[i]]],l[sa[i]]));
	// for(int i=2;i<=tot;i++)
		// cerr<<l[i]<<" ";cerr<<endl;
	for(int i=2;i<=tot;i++)
		mn[i]=min(mn[i],l[i]);//,cerr<<mn[i]<<" ";cerr<<endl;
}
int main()
{
	scanf("%d%s",&n,s+1);
	m=strlen(s+1);
	if(n==1)
	{
		printf("%d\n",m);
		return 0;
	}
	for(int i=1;i<=m;i++)
		ins(s[i]-'a',i);
	for(int i=2;i<=tot;i++)
		wsu[dis[i]]++;
	for(int i=1;i<=m;i++)
		wsu[i]+=wsu[i-1];
	for(int i=m;i>=1;i--)
		sa[wsu[dis[i]]--]=i;
	for(int i=2;i<=tot;i++)
		mn[i]=dis[i];//,cerr<<mn[i]<<" ";cerr<<endl;
	for(int i=2;i<=n;i++)
		wk();
	for(int i=2;i<=tot;i++)
		ans=max(ans,mn[i]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-04-15 20:08  lokiii  阅读(103)  评论(0编辑  收藏  举报