SPOJ LCS2 - Longest Common Substring II 字符串 SAM

时间:2023-03-08 16:29:33
SPOJ LCS2 - Longest Common Substring II 字符串 SAM

原文链接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过这题??

看这个:

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

SPOJ LCS2 - Longest Common Substring II 字符串 SAM

SPOJ LCS2 - Longest Common Substring II 字符串 SAM