SuffixAutomation - 重拾。

Table of Contents

  1. LCS - Longest Common Substring
    1. Description
    2. Solution
  2. LCS2 - Longest Common Substring
    1. Description
    2. Solution

LCS - Longest Common Substring

Description

给两个长度$\le 250000$的小写字母串, 求最长公共子串.

Solution

子串就是SAM了. 对其中一个建出SAM, 另一个在上面跑, 如果可以转移, 就直接转移, 长度加一, 否则跳父亲到可以转移, 自然是取$max_{\alpha}$了.

如果都不行就回到根.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

const int maxn = 700005;

int ans;
char s1[maxn], s2[maxn];

struct SuffixAutoMation
{
int tr[maxn][26], fa[maxn], mx[maxn];
int lst, cnt;
SuffixAutoMation() {
lst = cnt = 1;
}
void Insert(int c) {
int p = lst, np = ++cnt;
lst = np; mx[np] = mx[p] + 1;
for (; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
if (!p) fa[np] = 1;
else {
int q = tr[p][c];
if (mx[p] + 1 == mx[q]) fa[np] = q;
else {
int nq = ++cnt;
mx[nq] = mx[p] + 1;
for (int i = 0; i < 26; ++i) tr[nq][i] = tr[q][i];
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
for (; tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
}
}
//siz[np] = 1;
}
void Build(int n) {
for (int i = 1; i <= n; ++i)
Insert(s1[i] - 'a');
}
void Solve(int n) {
int pos = 1, len = 0;
for (int i = 1; i <= n; ++i) {
int ch = s2[i] - 'a';
if (tr[pos][ch]) len++, pos = tr[pos][ch];
else {
for (; pos && !tr[pos][ch]; ) pos = fa[pos];
if (pos) len = mx[pos] + 1, pos = tr[pos][ch];
else pos = 1, len = 0;
}
ans = max(ans, len);
}
}
}SAM;

int main() {
scanf("%s%s", s1 + 1, s2 + 1);
int l1 = strlen(s1 + 1), l2 = strlen(s2 + 1);
SAM.Build(l1);
SAM.Solve(l2);
printf("%d\n", ans);
return 0;
}

LCS2 - Longest Common Substring

Description

还是小写字符串$(\le 100000)$, 不过增加到了$10$个.

Solution

建一个SAM, 其他的分别在上面跑, 求出$ans_{\alpha}\{2, 3, 4, \ldots,n\}$, 和$max_{\alpha}$取个min.

注意实现细节.

由于跑了很多串, 可能出现匹配位置不同的情况, 注意parent树节点的儿子的答案可以拿来取max. 用$max$的拓扑性就好了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

const int maxn = 3e+5 + 5;

int n, ans;
int a[maxn], b[maxn];
char s[maxn];

struct SuffixAutoMation
{
int tr[maxn][26], mx[maxn], sz[maxn], fa[maxn];
int lst, cnt;
int mxv[maxn], mnv[maxn];
SuffixAutoMation() {
lst = cnt = 1;
}
void Insert(int c) {
int p = lst, np = ++cnt;
lst = np; mx[np] = mx[p] + 1;
for (; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
if (!p) fa[np] = 1;
else {
int q = tr[p][c];
if (mx[p] + 1 == mx[q]) fa[np] = q;
else {
int nq = ++cnt;
mx[nq] = mx[p] + 1;
for (int i = 0; i < 26; ++i) tr[nq][i] = tr[q][i];
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
for (; tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
}
}
sz[np] = 1;
}
void RadixSort() {
memset(b, 0, sizeof b);
for (int i = 1; i <= cnt; ++i) b[mx[i]]++;
for (int i = 1; i <= cnt; ++i) b[i] += b[i - 1];
for (int i = 1; i <= cnt; ++i) a[b[mx[i]]--] = i;
}
void Build(int n) {
for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
memset(mnv, 0x3f, sizeof mnv);
}
void Work(int n) {
int p = 1, len = 0;
//memset(mxv, 0, sizeof mxv);
for (int i = 1; i <= n; ++i) {
int ch = s[i] - 'a';
if (tr[p][ch]) p = tr[p][ch], len++, mxv[p] = max(mxv[p], len);
else {
for (; p && !tr[p][ch]; ) p = fa[p];
if (p) len = mx[p] + 1, p = tr[p][ch], mxv[p] = max(mxv[p], len);
else p = 1, len = 0;
}
}
for (int i = cnt; i; --i) {
int x = a[i], f = fa[x];
mxv[f] = max(mxv[f], min(mxv[x], mx[f]));
mnv[x] = min(mnv[x], mxv[x]);
mxv[x] = 0;
}
}
void CheckAns() {
for (int i = 1; i <= cnt; ++i) ans = max(ans, mnv[i]);
}
}SAM;

int main() {
scanf("%s", s + 1);
//memset(mnv, 0x3f, sizeof mnv);
int l = strlen(s + 1);
SAM.Build(l);
SAM.RadixSort();
while (scanf("%s", s + 1) != EOF) {

l = strlen(s + 1);
//if (!l) break;
++n;
SAM.Work(l);
}
SAM.CheckAns();
printf("%d\n", ans);
return 0;
}