SuffixAutomation - 重拾。
Table of Contents
- LCS - Longest Common Substring
- Description
- Solution
- LCS2 - Longest Common Substring
- Description
- 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; } } } 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; 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); int l = strlen(s + 1); SAM.Build(l); SAM.RadixSort(); while (scanf("%s", s + 1) != EOF) { l = strlen(s + 1); ++n; SAM.Work(l); } SAM.CheckAns(); printf("%d\n", ans); return 0; }
|