SP1812 LCS2 - Longest Common Substring II

还是求几个串的最长公共串。

但是由于数据范围的原因,用后缀数组可能会T,所以现在介绍一种后缀自动机的解法。

首先考虑两个串的公共串怎么求(假设分别为 \(S\)\(T\))。对 \(S\) 建立后缀自动机,把 \(T\) 放在后缀自动机上跑,如果没有转移就跳后缀 \(link\)。然后在每个节点记录一个最大值最后取个 \(\max\) 就行。

现在你有很多串。对第一个串建SAM,然后其余每个串在SAM上跑一遍,还是每个节点存一下公共串的最大值,注意 \(parent\) 树上的每个子孙对祖先都有贡献,然后就做完了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;

const int INF=1<<30,N=3000000;
char s[N];
int ans[N];
struct Suffix_DFA
{
	int head[N],cnt,Siz,last,Max[N];
	struct SAM
	{
		int link,ch[30],len;
	}sam[N];
	struct Edge
	{
		int nxt,to;
	}g[N*2];
	
	void add(int from,int to)
	{
		g[++cnt].nxt=head[from];
		g[cnt].to=to;
		head[from]=cnt;
	}
	
	void SAM_Extend(int k)
	{
		int cur=++Siz;
		sam[cur].len=sam[last].len+1;
		int p=last;
		while(p!=-1&&!sam[p].ch[k])
			sam[p].ch[k]=cur,
			p=sam[p].link;
		if(p==-1)
			sam[cur].link=0;
		else
		{
			int q=sam[p].ch[k];
			if(sam[q].len==sam[p].len+1)
				sam[cur].link=q;
			else
			{
				int clone=++Siz;
				sam[clone].len=sam[p].len+1;
				sam[clone].link=sam[q].link;
				for (int i=0;i<26;i++)
					sam[clone].ch[i]=sam[q].ch[i];
				while(p!=-1&&sam[p].ch[k]==q)
					sam[p].ch[k]=clone,
					p=sam[p].link;
				sam[cur].link=sam[q].link=clone;
			}
		}
		last=cur;
	}
	
	void build()
	{
		for (int i=1;i<=Siz;i++)
			add(sam[i].link,i);
	}
	
	void DFS(int x)
	{
		for (int i=head[x];i;i=g[i].nxt)
		{
			int v=g[i].to;
			DFS(v);
			Max[x]=min(sam[x].len,max(Max[x],Max[v]));
		}
		ans[x]=min(ans[x],Max[x]);
	}
	
	void work(char s[],int len)
	{
		memset(Max,0,sizeof(Max));
		int l=0,k=0;
		for (int i=1;i<=len;i++)
		{
			int t=s[i]-'a';
			while(k&&!sam[k].ch[t])
				k=sam[k].link,
				l=sam[k].len;
			if(sam[k].ch[t])
				l++,k=sam[k].ch[t];
			Max[k]=max(Max[k],l);
		}
		DFS(0);
	}
}S;

void work()
{
	S.sam[0].link=-1;
	scanf("%s",s+1);
	int len=strlen(s+1);
	for (int i=1;i<=len;i++)
		S.SAM_Extend(s[i]-'a');
	S.build();
	for (int i=1;i<=S.Siz;i++)
		ans[i]=INF;
	while(~scanf("%s",s+1))
	{
		len=strlen(s+1);
		S.work(s,len);
	}
	int Ans=0;
	for (int i=1;i<=S.Siz;i++)
		Ans=max(Ans,ans[i]);
	printf("%d\n",Ans);
}

int main()
{
	work();
	return 0;
}
posted @ 2020-07-22 23:38  With_penguin  阅读(74)  评论(0编辑  收藏  举报