BZOJ2946 SPOJ1812:Longest Common Substring II(后缀自动机)

SPOJ1812

BZOJ2946

 

题意:给若干字符串,求它们的最长公共子串的长度。

 

 

题解:后缀自动机。

         对第一个串建立SAM,用后面的串分别匹配。

         对于SAM,每个节点新增两个值MIN,MAX;

         MIN代表该节点满足所有字符串的最大值,初始化为该字符串建立时的最大值,每次匹配后更新。

         MAX代表该节点满足单一字符串时的最大值,匹配完一个字符串后重置为0

         每次匹配完字符串,按照拓扑序从后往前更新(保证父节点在子节点后被更新)

         用子节点的MAX更新父节点的MAX父节点是子节点的后缀,子节点能匹配的长度只会大等于父节点。)

         最后求MIN的最大值即可。

 

代码:

#include<bits/stdc++.h>
using namespace std;
const int Maxn=2e5+50;
inline int read()
{
	char ch=getchar();int i=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
	return i*f;
}

int n,m,m1;
char s[Maxn];

struct sam
{
	int last,cnt,rt,son[Maxn][26],link[Maxn],len[Maxn],mx[Maxn],mn[Maxn],que[Maxn],c[Maxn];
	sam()
	{
		last=rt=++cnt;len[0]=-1;
		for(int i=0;i<=25;i++)son[0][i]=1;
	}
	inline void extend(int c)
	{
		int np=++cnt,p=last;last=cnt;
		len[np]=mn[np]=len[p]+1;
		while(p&&!son[p][c])son[p][c]=np,p=link[p];
		if(!p)link[np]=rt;
		else
		{
			int q=son[p][c];
			if(len[q]==len[p]+1)link[np]=q;
			else
			{
				int nq=++cnt;len[nq]=mn[nq]=len[p]+1;
				memcpy(son[nq],son[q],sizeof(int)*26);
				link[nq]=link[q];link[q]=link[np]=nq;
				while(p&&son[p][c]==q)son[p][c]=nq,p=link[p];
			}
		}
	}
	inline void update()
	{
		m=strlen(s+1);
		memset(mx,0,sizeof(mx));
		int nowpos=1,nowlen=0;
		for(int i=1;i<=m;i++)
		{
			int c=s[i]-'a';
			if(son[nowpos][c])
			{
				nowpos=son[nowpos][c];nowlen++;
				mx[nowpos]=max(mx[nowpos],nowlen);
			}
			else
			{
				while(!son[nowpos][c])nowpos=link[nowpos];
				nowlen=len[nowpos]+1;nowpos=son[nowpos][c];
				mx[nowpos]=max(mx[nowpos],nowlen);
			}
		}
		memset(c,0,sizeof(c));
		for(int i=1;i<=cnt;i++)c[len[i]]++;
		for(int i=1;i<=m1;i++)c[i]+=c[i-1];
		for(int i=cnt;i>=1;i--)que[c[len[i]]--]=i;
		for(int i=cnt;i>=1;i--)
		{
			if(link[que[i]])mx[link[que[i]]]=min(max(mx[link[que[i]]],mx[que[i]]),mn[link[que[i]]]);
			mn[que[i]]=min(mn[que[i]],mx[que[i]]);
		}
	}
}sam;

int main()
{
	scanf("%d",&n);scanf("%s",s+1);m1=strlen(s+1);
	for(int i=1;i<=m1;i++)sam.extend(s[i]-'a');
	while(scanf("%s",s+1)!=EOF)sam.update();
	int ans=0;
	for(int i=sam.cnt;i>=1;i--)ans=max(ans,sam.mn[i]);
	printf("%d\n",ans);
}



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值