题目链接:https://www.spoj.com/problems/LCS2/en/
解题思路:
对于两个串的LCS,我们只需对其中一个建sam,另一个在上面跑,记录最大值即可。
对于多个串的话,我们需要记录每个串跑sam的时候在每个节点的相同子串取最大值,每次跑完更新一下每个节点当前所有串的最长相同子串取最小值。
需要注意的是失配时跳fa数组的时候也需要更新fa节点的数据。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define pf push_front
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)
//const double pi = acos(-1.0);
/*x = x*cosB-y*sinB , y = y*cosB-x*sinB*/
const int N = 1e5+5;
char s[N];
const int MAXN = 1e5+5;
const int ALP = 26;
struct SAM
{
int trie[MAXN<<1][ALP];
int len[MAXN<<1];
int fa[MAXN<<1];
int sz,las;
int LCS[MAXN<<1];
int nowlcs[MAXN<<1];
void init(){
newnode(las=sz=0);
fa[0] = -1;
}
int newnode(int x){
memset(trie[x],0,sizeof trie[x]);
len[x] = 0;
LCS[x] = INF;
return sz++;
}
int idx(char ch){
return ch-'a';
}
void add(char ch){
int c = idx(ch);
int p = las;
int np = newnode(sz);
las = np;
len[np] = len[p]+1;
for(;~p && !trie[p][c];p=fa[p]) trie[p][c] = np;
if (p==-1) fa[np] = 0;
else {
int q = trie[p][c];
if (len[q]==len[p]+1) fa[np] = q;
else {
int nq = newnode(sz);
fa[nq] = fa[q];
for0(i,0,ALP) trie[nq][i] = trie[q][i];
len[nq] = len[p]+1;
fa[np] = fa[q] = nq;
for (;~p && trie[p][c]==q;p=fa[p]) trie[p][c] = nq;
}
}
}
void find(char *s){//跑第2~n个串
memset(nowlcs,0,sizeof nowlcs);
int u=0,nowlen=0;
for (int i=0;s[i];i++){
int c = idx(s[i]);
while (~u && !trie[u][c]){
u = fa[u];
if (~u) nowlen = len[u],nowlcs[u] = max(nowlcs[u],nowlen);
}
if (u==-1) {nowlen = u = 0;continue;}
u = trie[u][c];
nowlen++;
nowlcs[u] = max(nowlcs[u],nowlen);
}
for0(i,0,sz) LCS[i] = min(LCS[i],nowlcs[i]);
}
int solve(){//取每个节点最长子串的最大值
int ans = 0;
for0(i,0,sz) /*if (LCS[i]!=INF)*/ans = max(ans,LCS[i]);
return ans;
}
}sam;
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
scanf("%s",s);
sam.init();
for (int i=0;s[i];i++) sam.add(s[i]);
while (~scanf("%s",s)) sam.find(s);
printf("%d\n",sam.solve());
return 0;
}