LCS&LCS2 SAM SPOJ
阿新 • • 發佈:2018-11-21
LCS - Longest Common Substring
Description
給兩個長度\(\le 250000\)的小寫字母串, 求最長公共子串.
Solution
子串就是SAM了. 對其中一個建出SAM, 另一個在上面跑, 如果可以轉移, 就直接轉移, 長度加一, 否則跳父親到可以轉移, 自然是取\(max_{\alpha}\)了.
如果都不行就回到根.
#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\)的拓撲性就好了.
#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; }