http://www.elijahqi.win/archives/2855
Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
Input
文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3
abcb
bca
acbc
Sample Output
2
求多个串的最长公共子串 首先针对第一个串建立sam 设mx[x]表示所有串在这个节点匹配的最长距离是多少 tmp[x]表示当前这个串和第一个串在该节点的最长匹配是多少
因为一次匹配不可能更新所有的tmp值所以我们需要在匹配之后拓扑排序之后 因为父节点肯定是当前节点的后缀所以匹配长度不会减少 然后每次更新完tmp之后再去更新mx
len[fa[p]]
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
char s[N];int n,len[N<<1],fa[N<<1],cnt=1,root=1,last=1;
int c[N],tmp[N<<1],mx[N<<1],ans,ch[N<<1][26],rk[N<<1];
inline void insert1(int x){
int np=++cnt,p=last;len[np]=len[p]+1;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=root;else{
int q=ch[p][x];if (len[p]+1==len[q]) fa[np]=q;else{
int nq=++cnt;fa[nq]=fa[q];memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[q]=fa[np]=nq;len[nq]=len[p]+1;for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}last=np;
}
int main(){
freopen("spoj1812.in","r",stdin);
int T;scanf("%d",&T);
scanf("%s",s+1);n=strlen(s+1);
for (int i=1;i<=n;++i) insert1(s[i]-'a');
for (int i=1;i<=cnt;++i) ++c[len[i]];
for (int i=1;i<=n;++i) c[i]+=c[i-1];memset(mx,0x3f,sizeof(mx));
for (int i=cnt;i;--i) rk[c[len[i]]--]=i;
while(~scanf("%s",s+1)){int m=strlen(s+1);
memset(tmp,0,sizeof(tmp));int now=1,l=0;
for (int i=1;i<=m;++i){
if (ch[now][s[i]-'a']) ++l,now=ch[now][s[i]-'a'];
else{
while(now&&!ch[now][s[i]-'a']) now=fa[now];
if (!now) now=1,l=0;else l=len[now]+1,now=ch[now][s[i]-'a'];
}tmp[now]=max(tmp[now],l);
}
for (int i=cnt;i;--i){
int x=rk[i];mx[x]=min(mx[x],tmp[x]);
if(fa[x]&&tmp[x]) tmp[fa[x]]=len[fa[x]];
}
}for (int i=1;i<=cnt;++i) ans=max(ans,mx[i]);printf("%d\n",ans);
return 0;
}