Longest Common Substring II SPOJ - LCS2(廣義sam)
阿新 • • 發佈:2020-10-14
題意:多個串的最大公共子串
思路:用廣義字尾陣列構造後,記錄每個點包含的節點,最後判斷那個點包含所有點。
#include <bits/stdc++.h> using namespace std; #define MAXN 2000000 // 雙倍字串長度 #define CHAR_NUM 30 // 字符集個數,注意修改下方的 (-'a') #define NUM 15 // 字串個數 struct exSAM { int len[MAXN]; // 節點長度 int link[MAXN]; // 字尾連結,link int next[MAXN][CHAR_NUM]; // 轉移 int tot; // 節點總數:[0, tot) int lenSorted[MAXN]; // 按照 len 排序後的陣列,僅排序 [1, tot) // 部分,最終下標範圍 [0, tot - 1) int sizeC[MAXN][NUM]; // 表示某個字串的子串個數 int curString; // 字串實際個數 /** * 計數排序使用的輔助空間陣列 */ int lc[MAXN]; // 統計個數 void init() { tot = 1; link[0] = -1; } int insertSAM(int last, int c) { int cur = next[last][c]; len[cur] = len[last] + 1; int p = link[last]; while (p != -1) { if (!next[p][c]) next[p][c] = cur; else break; p = link[p]; } if (p == -1) { link[cur] = 0; return cur; } int q = next[p][c]; if (len[p] + 1 == len[q]) { link[cur] = q; return cur; } int clone = tot++; for (int i = 0; i < CHAR_NUM; ++i) next[clone][i] = len[next[q][i]] != 0 ? next[q][i] : 0; len[clone] = len[p] + 1; while (p != -1 && next[p][c] == q) { next[p][c] = clone; p = link[p]; } link[clone] = link[q]; link[cur] = clone; link[q] = clone; return cur; } int insertTrie(int cur, int c) { if (!next[cur][c]) next[cur][c] = tot++; sizeC[next[cur][c]][curString]++; return next[cur][c]; } void insert(const string &s) { int root = 0; for (auto ch : s) root = insertTrie(root, ch - 'a'); curString++; } void insert(const char *s, int n) { int root = 0; for (int i = 0; i < n; ++i) root = insertTrie(root, s[i] - 'a'); curString++; } void build() { queue<pair<int, int>> q; for (int i = 0; i < 26; ++i) if (next[0][i]) q.push({i, 0}); while (!q.empty()) { auto item = q.front(); q.pop(); auto last = insertSAM(item.second, item.first); for (int i = 0; i < 26; ++i) if (next[last][i]) q.push({i, last}); } } void sortLen() { for (int i = 1; i < tot; ++i) lc[i] = 0; for (int i = 1; i < tot; ++i) lc[len[i]]++; for (int i = 2; i < tot; ++i) lc[i] += lc[i - 1]; for (int i = 1; i < tot; ++i) lenSorted[--lc[len[i]]] = i; } void getSizeLen() { for (int i = tot - 2; i >= 0; --i) for (int j = 0; j < curString; ++j) sizeC[link[lenSorted[i]]][j] += sizeC[lenSorted[i]][j]; } void debug() { cout << " i len link "; for (int i = 0; i < 26; ++i) cout << " " << (char)('a' + i); cout << endl; for (int i = 0; i < tot; ++i) { cout << "i: " << setw(3) << i << " len: " << setw(3) << len[i] << " link: " << setw(3) << link[i] << " Next: "; for (int j = 0; j < CHAR_NUM; ++j) { cout << setw(3) << next[i][j]; } cout << endl; } } } exSam; int main() { exSam.init(); string s; while (cin >> s) exSam.insert(s); exSam.build(); exSam.sortLen(); exSam.getSizeLen(); int ans = 0; for (int i = 0; i < exSam.tot; ++i) { bool flag = true; for (int j = 0; j < exSam.curString; ++j) { if (!exSam.sizeC[i][j]) { flag = false; break; } } if (flag) ans = max(ans, exSam.len[i]); } cout << ans << endl; }