用于快速求解字符串子串问题。
$endpos$ 是一个子串对应的一个集合,该子串在原串出现,最后一个字符的位置。
如原串 abcbc
:
abcbc
,bcbc
,cbc
的 $endpos$ 都是 $5$ 。
bc
,c
的 $endpos$ 都是 $3, 5$ 。
在 SAM 的一个节点中,对应了很多个子串,它们是一个串的后缀。$len$ 表示这些子串中最大长度。$fa$ 表示将这些子串中最短的一个去除第一个字符后的串对应的节点。
abcbc
中,一个节点有 abcbc
,bcbc
,cbc
,$len$ 为 $5$ ,$fa$ 为 bc
对应的节点。一个节点的所有儿子都是以这个节点为后缀的串。
查看代码
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
| #include <cstdio> #include <vector> #include <cstring> #include <algorithm> using namespace std; const int N = 2e6 + 10; char str[N]; int n, last = 1, cnt = 1; vector<int> g[N]; struct Node { int p, len, nxt[26]; } tr[N]; void extend (int c) { int p = last, np = ++cnt; tr[np].len = tr[p].len + 1; for (; p && !tr[p].nxt[c]; p = tr[p].p) tr[p].nxt[c] = np; last = np; if (!p) return void(tr[np].p = 1); int q = tr[p].nxt[c]; if (tr[q].len == tr[p].len + 1) return void(tr[np].p = q); int nq = ++cnt; tr[nq] = tr[q], tr[nq].len = tr[p].len + 1; tr[q].p = tr[np].p = nq; for (; p && tr[p].nxt[c] == q; p = tr[p].p) tr[p].nxt[c] = nq; } void dfs(int x) { for (int i : g[x]) { dfs(i); } } int main() { scanf("%s", str + 1); n = strlen(str + 1); for (int i = 1; i <= n; i++) extend (str[i] - 'a'); for (int i = 2; i <= cnt; ++i) g[tr[i].p].push_back(i); dfs(1); return 0; }
|