Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

后缀自动机(SAM)模板

用于快速求解字符串子串问题。

$endpos$ 是一个子串对应的一个集合,该子串在原串出现,最后一个字符的位置。

如原串 abcbc

  • abcbcbcbccbc 的 $endpos$ 都是 $5$ 。
  • bcc 的 $endpos$ 都是 $3, 5$ 。

在 SAM 的一个节点中,对应了很多个子串,它们是一个串的后缀。$len$ 表示这些子串中最大长度。$fa$ 表示将这些子串中最短的一个去除第一个字符后的串对应的节点。

abcbc 中,一个节点有 abcbcbcbccbc ,$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;
}