Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
Input
文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3
abcb
bca
acbc
Sample Output
2
分析
先对第一个字符串构建sam,然后对于其余每个串,记录每个位置在后缀自动机上匹配到的最大长度,然后有一个特别关键的转移就是每个儿子节点的mx传给其parents,再在所有位置里面找一个最大的即可。
代码
#include <bits/stdc++.h>
const int N = 4005;
int ch[N][30],max[N],fa[N];
int last,cnt;
void extand(int x)
{
int p,q,np,nq;
p = last, np = last = ++cnt;
max[np] = max[p] + 1;
for (; !ch[p][x] && p; p = fa[p])
ch[p][x] = np;
if (!p)
fa[np] = 1;
else
{
q = ch[p][x];
if (max[q] == max[p] + 1)
fa[np] = q;
else
{
nq = ++cnt;
max[nq] = max[p] + 1;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[nq] = fa[q];
fa[q] = fa[np] = nq;
for (; ch[p][x] == q; p = fa[p])
ch[p][x] = nq;
}
}
}
int n,m;
int b[N],c[N];
int mx[N];
int ans[N];
char s[N];
void preWork()
{
for (int i = 1; i <= cnt; i++)
b[max[i]]++;
for (int i = 2; i <= n; i++)
b[i] += b[i - 1];
for (int i = 1; i <= cnt; i++)
c[b[max[i]]--] = i;
for (int i = 1; i <= cnt; i++)
ans[i] = max[i];
}
void solve()
{
memset(mx, 0, sizeof(mx));
int now = 1, len = 0;
for (int i = 1; i <= n; i++)
{
s[i] -= 'a';
for (; !ch[now][s[i]] && now; now = fa[now]);
if (!now)
now = 1, len = 0;
else len = std::min(len, max[now]) + 1, now = ch[now][s[i]];
mx[now] = std::max(mx[now], len);
}
for (int i = cnt; i >= 1; i--)
mx[fa[c[i]]] = std::max(mx[fa[c[i]]], mx[c[i]]);
for (int i = 1; i <= cnt; i++)
ans[i] = std::min(ans[i], mx[i]);
}
int main()
{
scanf("%d",&m);
scanf("%s",s + 1);
n = strlen(s + 1);
last = cnt = 1;
for (int i = 1; i <= n; i++)
extand(s[i] - 'a');
preWork();
for (int i = 1; i < m; i++)
{
scanf("%s", s + 1);
n = strlen(s + 1);
solve();
}
int w = 0;
for (int i = 1; i <= cnt; i++)
w = std::max(w, ans[i]);
printf("%d\n",w);
}