SPOJ LCS2

原文链接http://www.cnblogs.com/zhouzhendong/p/8982484.html

题目传送门 - SPOJ LCS2

题意

  求若干$(若干<10)$个字符串的最长公共连续子串长度。

  串长$leq 100000$

题解

  建议在做本题之前,先去做SPOJ LCS,本题是其升级版。

  题解链接 - SPOJ LCS - http://www.cnblogs.com/zhouzhendong/p/8982392.html

  对于本题,我们只需要保持一下之后每一个串在第一个串的$SAM$的每一个状态上的最大匹配长度,然后最后对于每一个状态,取$min(该状态的Max值,其他所有字符串在该状态上面的最大匹配长度的最小值)$即可。

  于是,我们先像SPOJ LCS一样,让所有串都走一遍,然后记录一下值。

  注意到,每一个状态的结果都会对其$fa$做贡献。

  于是我们可以基数排序预处理拓扑序,然后逆序更新即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int n,m=0,last=1,size=1,Max[15][N];
int id[N],tax[N];
char s[N];
struct SAM{
	int Next[26],fa,Max;
}t[N];
void expend(int c){
	int p=last,np=++size,q,nq;
	t[np].Max=t[p].Max+1;
	for (;!t[p].Next[c];p=t[p].fa)
		t[p].Next[c]=np;
	q=t[p].Next[c];
	if (t[q].Max==t[p].Max+1)
		t[np].fa=q;
	else {
		nq=++size;
		t[nq]=t[q],t[nq].Max=t[p].Max+1;
		t[q].fa=t[np].fa=nq;
		for (;t[p].Next[c]==q;p=t[p].fa)
			t[p].Next[c]=nq;
	}
	last=np;
}
int main(){
	t[0].Max=-1;
	for (int i=0;i<26;i++)
		t[0].Next[i]=1;
	gets(s);
	n=strlen(s);
	for (int i=0;i<n;i++)
		expend(s[i]-'a');
	for (int i=1;i<=size;i++)
		tax[t[i].Max]++;
	for (int i=1;i<=size;i++)
		tax[i]+=tax[i-1];
	for (int i=1;i<=size;i++)
		id[tax[t[i].Max]--]=i;
	while (gets(s)&&strlen(s)){
		n=strlen(s);
		for (int i=0,now=1,len=0;i<n;i++){
			int c=s[i]-'a';
			if (t[now].Next[c]){
				len++;
				now=t[now].Next[c];
				Max[m][now]=max(Max[m][now],len);
				continue;
			}
			while (!t[now].Next[c])
				now=t[now].fa;
			len=t[now].Max+1;
			now=t[now].Next[c];
			Max[m][now]=max(Max[m][now],len);
		}
		for (int i=size;i>=1;i--)
			Max[m][t[id[i]].fa]=max(Max[m][t[id[i]].fa],Max[m][id[i]]);
		m++;
	}
	int ans=0;
	for (int i=1;i<=size;i++){
		int now=t[i].Max;
		for (int j=0;j<m;j++)
			now=min(now,Max[j][i]);
		ans=max(ans,now);
	}
	printf("%d",ans);
	return 0;
}

UPD(2018-05-07):

  听说有人要用hash过这题??

看这个:

来自陈立杰的后缀自动机课件

原文地址:https://www.cnblogs.com/zhouzhendong/p/SPOJ-LCS2.html