参考资料:(APIO2018)从DFA到后缀自动机_张云帆
又一个学了很多遍都不会的算法/数据结构……(话说我怎么每篇知识总结一开始都是这句话qwq)
先orz后缀自动机之神兔崽子TzzDzz(顺便喂它最喜欢吃的叶子)
OrzTzzDzz
前排提示:由于作者很菜,且本文的目标是快速理解并写出
(背过)
后缀自动机的构建过程,所以将会省略很多结论的证明,语言也有不严谨之处,敬请谅解。如感兴趣可以参考文首提到的参考资料。阅读前建议先了解AC自动机。
(后缀自动机之神Tzz自己把后缀自动机玩得炉火纯青,给别人讲时却就让别人背代码。如果您不幸受到了Tzz的教育
(误导)
,请可以直接拉到最下方看代码
然后把他嘴一顿
)。
零、前言
很多字符串题要处理与子串有关的问题。如果想在AC自动机上处理一个字符串的所有子串,通常要将它的所有后缀插入(子串就是后缀的前缀),时空复杂度是
O
(
n
2
)
O(n^2)
O
(
n
2
)
的。于是,我们需要充分利用插入的所有串都是同一个串的后缀这一性质来优化。后缀自动机,请。
(其实是Tzz太神仙了,自称不会KMP和AC自动机,见到所有字符串题都后缀自动机+哈希秒掉)
一、后缀自动机的相关定义和性质
以字符串”aabab”为例(下标从
0
0
0
开始)。
一个字符串的
R
i
g
h
t
Right
R
i
g
h
t
集合:该字符串
所有
出现位置的右端点的集合。如字符串”ab”的
R
i
g
h
t
Right
R
i
g
h
t
集合是
{
2
,
4
}
\{2,4\}
{
2
,
4
}
,字符串”a”的
R
i
g
h
t
Right
R
i
g
h
t
集合是
{
0
,
1
,
3
}
\{0,1,3\}
{
0
,
1
,
3
}
。定义空串的
R
i
g
h
t
Right
R
i
g
h
t
集合是
{
i
∣
−
1
≤
i
<
n
}
\{i|-1\leq i < n\}
{
i
∣
−
1
≤
i
<
n
}
,
−
1
-1
−
1
表示在第一个字符之前的空串。两个
R
i
g
h
t
Right
R
i
g
h
t
集合之间要么交集为空,要么一个是另一个的子集(三分律)。稍后证明。
等价:如果两个字符串的
R
i
g
h
t
Right
R
i
g
h
t
集合相同,那么称它们“等价”。如字符串”ab”和”b”的
R
i
g
h
t
Right
R
i
g
h
t
集合都是
{
2
,
4
}
\{2,4\}
{
2
,
4
}
,所以它们是等价的。
和AC自动机相比,后缀自动机把所有等价字符串都缩成一个结点(即每个结点表示一个
R
i
g
h
t
Right
R
i
g
h
t
集合,点
u
u
u
的
R
i
g
h
t
Right
R
i
g
h
t
集合记作
R
u
R_u
R
u
),是一个DAG(而不是像Trie树一样的树形结构)。稍后将证明,结点数不会超过
2
n
+
1
2n+1
2
n
+
1
,转移数不会超过
3
n
3n
3
n
。即后缀自动机的空间是线性的。
一个结点的
f
a
fa
f
a
:两个结点
u
u
u
和
v
v
v
,若
R
v
R_v
R
v
是
R
u
R_u
R
u
的真子集,且
u
′
u'
u
′
是所有这样的结点
u
u
u
中
R
i
g
h
t
Right
R
i
g
h
t
集合的元素最少的,则
u
′
u'
u
′
是
v
v
v
的
f
a
fa
f
a
。根(空串)的
f
a
fa
f
a
不存在。根据三分律,除了根外所有节点有且只有一个
f
a
fa
f
a
,所以会组成一棵
f
a
fa
f
a
树。
下面是对”aabab”建出的后缀自动机(鼠绘)(黑线是自动机的转移边,红线是
f
a
fa
f
a
,绿字是该结点对应的子串)。可以看出,所有子串都对应一个结点,但一个结点可能对应多个子串(图中用逗号分隔)。
请在可以独立画出这幅图后再继续阅读。
结点的
m
i
n
min
m
i
n
和
m
a
x
max
m
a
x
:该结点对应的子串长度的最小/最大值。例如上图中
{
4
}
\{4\}
{
4
}
的
m
i
n
min
m
i
n
是
3
3
3
,
m
a
x
max
m
a
x
是
4
4
4
。可以证明,结点对应的子串长度一定是一个连续的区间
[
m
i
n
,
m
a
x
]
[min,max]
[
m
i
n
,
m
a
x
]
。并且,如果
p
p
p
是
q
q
q
的
f
a
fa
f
a
,那么有
m
a
x
p
max_p
m
a
x
p
=
m
i
n
q
−
1
min_q-1
m
i
n
q
−
1
。(两个结论都可以反证。)脑补一下可以发现,
m
a
x
max
m
a
x
和
m
i
n
min
m
i
n
分别相当于从根节点到该结点的最长路和最短路的长度。不可能有两条路长度相等(想一想,为什么)。
下面来证明上面“稍后再证”的两个结论。
三分律:反证。设两不同状态为
p
p
p
和
q
q
q
,则三分律可表示为:
R
p
∩
R
q
=
∅
R_p\cap R_q=\emptyset
R
p
∩
R
q
=
∅
、
R
p
⊂
R
q
R_p\subset R_q
R
p
⊂
R
q
、
R
p
⊃
R
q
R_p\supset R_q
R
p
⊃
R
q
中有且只有一式成立。假设
R
p
∩
R
q
≠
∅
R_p \cap R_q \neq \emptyset
R
p
∩
R
q
̸
=
∅
,任取结束位置
r
∈
R
p
∩
R
q
r\in R_p \cap R_q
r
∈
R
p
∩
R
q
。根据“一个子串只对应一个结点”,
p
p
p
和
q
q
q
对应的子串没有交集,所以
[
m
i
n
p
,
m
a
x
p
]
[min_p,max_p]
[
m
i
n
p
,
m
a
x
p
]
和
[
m
i
n
q
,
m
a
x
q
]
[min_q,max_q]
[
m
i
n
q
,
m
a
x
q
]
没有交集。假设
m
a
x
p
<
m
i
n
q
max_p<min_q
m
a
x
p
<
m
i
n
q
,由因为它们具有相同的结束位置(
r
r
r
集合中的元素),则
p
p
p
对应的串是
q
q
q
对应的串的后缀。所以
R
p
⊂
R
q
R_p \subset R_q
R
p
⊂
R
q
。同理,当
m
a
x
q
<
m
i
n
p
max_q<min_p
m
a
x
q
<
m
i
n
p
,能推出
R
p
⊃
R
q
R_p \supset R_q
R
p
⊃
R
q
。
结点数不超过
2
n
+
1
2n+1
2
n
+
1
:设根结点
R
i
g
h
t
Right
R
i
g
h
t
集合大小为
i
i
i
的
f
a
fa
f
a
树的最大结点数为
f
(
i
)
f(i)
f
(
i
)
,则
f
(
i
)
=
∑
j
=
1
k
f
(
x
j
)
+
1
(
∑
j
=
1
k
x
j
≤
i
)
f(i)=\sum_{j=1}^k f(x_j)+1(\sum_{j=1}^k x_j\leq i)
f
(
i
)
=
∑
j
=
1
k
f
(
x
j
)
+
1
(
∑
j
=
1
k
x
j
≤
i
)
。由于
f
(
x
j
)
f(x_j)
f
(
x
j
)
都是正的,所以就相当于
f
(
i
)
≤
∑
j
=
1
k
f
(
x
j
)
+
1
(
∑
j
=
1
k
x
j
=
i
)
f(i)\leq \sum_{j=1}^k f(x_j)+1(\sum_{j=1}^k x_j=i)
f
(
i
)
≤
∑
j
=
1
k
f
(
x
j
)
+
1
(
∑
j
=
1
k
x
j
=
i
)
。当
i
=
1
i=1
i
=
1
显然满足
f
(
i
)
=
1
=
2
n
−
1
f(i)=1=2n-1
f
(
i
)
=
1
=
2
n
−
1
,当
i
=
1
i=1
i
=
1
时脑补一下归纳,若对于任意
f
(
j
)
(
j
<
i
)
f(j)(j<i)
f
(
j
)
(
j
<
i
)
都成立,则有
f
(
i
)
≤
∑
j
=
1
k
f
(
x
j
)
≤
2
∑
j
=
1
k
x
j
−
k
<
2
i
−
1
f(i)\leq \sum_{j=1}^k f(x_j)\leq 2\sum_{j=1}^k x_j-k<2i-1
f
(
i
)
≤
∑
j
=
1
k
f
(
x
j
)
≤
2
∑
j
=
1
k
x
j
−
k
<
2
i
−
1
。而根节点(空串)
R
i
g
h
t
Right
R
i
g
h
t
集合结点数为
n
+
1
n+1
n
+
1
,所以结点数不超过
f
(
n
+
1
)
=
2
(
n
+
1
)
−
1
=
2
n
+
1
f(n+1)=2(n+1)-1=2n+1
f
(
n
+
1
)
=
2
(
n
+
1
)
−
1
=
2
n
+
1
。
转移数不超过
3
n
3n
3
n
:首先求一棵自动机的树形图,边数是点数减
1
1
1
,即不超过
2
n
2n
2
n
。(证明待续……)
二、后缀自动机的构建
使用增量法。每次插入一个字符。
设正在插入第
n
n
n
个字符
c
c
c
。
n
n
n
位置在当前后缀自动机中没有出现过,所以不会造成已有状态的合并,且一定会出现(至少)一个新结点,对应
R
i
g
h
t
Right
R
i
g
h
t
为
{
n
}
\{n\}
{
n
}
,对应的最长串长度(
m
a
x
max
m
a
x
)为
n
n
n
。
更进一步考虑新插入的结点会影响到原来的哪些结点。新字符只会与以
n
−
1
n-1
n
−
1
结尾的子串形成新的子串,所以受影响的一定是
R
i
g
h
t
Right
R
i
g
h
t
集合包含
n
−
1
n-1
n
−
1
的结点,即
R
i
g
h
t
Right
R
i
g
h
t
集合为
{
n
−
1
}
\{n-1\}
{
n
−
1
}
的结点(一定存在)在
f
a
fa
f
a
树上到根的链。同时,如果这些结点本来就有
c
c
c
字符的转移边,那么这些转移边指向的结点也会受影响(可能
R
i
g
h
t
Right
R
i
g
h
t
集合会增大)。下面无特殊说明的情况下,原串指前
n
−
1
n-1
n
−
1
个字符组成的字符串,
n
p
np
n
p
是新建的
R
i
g
h
t
Right
R
i
g
h
t
为
{
n
}
\{n\}
{
n
}
的结点,
p
p
p
是一个
R
i
g
h
t
Right
R
i
g
h
t
集合包含
n
−
1
n-1
n
−
1
的结点,
q
q
q
是
p
p
p
的
c
c
c
字符转移边指向的点(如果存在)。
如果
p
p
p
没有
c
c
c
字符的转移边,由于现在有了,直接向
n
p
np
n
p
连边即可。
如果一直到根都没有
c
c
c
字符的转移边,说明
c
c
c
字符在前
n
−
1
n-1
n
−
1
个字符中没有出现过。把
n
p
np
n
p
的
f
a
fa
f
a
置为根,插入结束。
如果
p
p
p
可以通过
c
c
c
字符转移到
q
q
q
,且深度最大,那么进行如下讨论。
状态的“分裂”是指由于加入了新的字符导致原来等价的字符串不再等价。如”aabca”中”aab”、”ab”和”b”是等价的,在后面加入一个’b’后”ab”和”b”等价,但不与”aab”等价。脑补一下,分裂时分出的每个部分的长度都是连续的区间,字符串长度越短
R
i
g
h
t
Right
R
i
g
h
t
集合越大。
前面提到过,
m
a
x
p
max_p
m
a
x
p
就是根到
p
p
p
的最长路,所以
m
a
x
q
≥
m
a
x
p
+
1
max_q\geq max_p+1
m
a
x
q
≥
m
a
x
p
+
1
。
如果
m
a
x
p
+
1
=
m
a
x
q
max_p+1=max_q
m
a
x
p
+
1
=
m
a
x
q
,说明原来
p
p
p
中对应的最长串后面加一个字符
c
c
c
形成了
q
q
q
对应的最长串。所以,所有
p
p
p
最长串的后缀加上一个字符
c
c
c
都是
q
q
q
最长串的后缀。
q
q
q
对应的子串集合是这些后缀的子集。因此
q
q
q
不会分裂,只是
R
i
g
h
t
Right
R
i
g
h
t
集合中增加了一个
n
n
n
。
如果
m
a
x
p
+
1
<
m
a
x
q
max_p+1<max_q
m
a
x
p
+
1
<
m
a
x
q
,说明在
q
q
q
的最长串不是由
p
p
p
途径转移而来的,所以
q
q
q
的最长串去掉最后一个字符
c
c
c
不是原串的后缀。加入一个字符
c
c
c
后,只有从
p
p
p
转移到
q
q
q
的子串的出现位置会增加,而比
m
a
x
p
+
1
max_p+1
m
a
x
p
+
1
还长的那部分不会变。所以此时
q
q
q
分裂为两个结点。称
R
i
g
h
t
Right
R
i
g
h
t
集合更新(即包含
{
n
}
\{n\}
{
n
}
的结点)为
n
q
nq
n
q
。
脑补一下,
n
q
nq
n
q
的
R
i
g
h
t
Right
R
i
g
h
t
集合是原来
q
q
q
的
R
i
g
h
t
Right
R
i
g
h
t
加上
n
n
n
,所以它应当成为
q
q
q
的
f
a
fa
f
a
。而
n
q
nq
n
q
的
f
a
fa
f
a
应当是
q
q
q
原本的
f
a
fa
f
a
。因为
n
q
nq
n
q
中所有串都是
p
p
p
加上一个字符
c
c
c
得到的,所以
m
a
x
n
q
=
m
a
x
p
+
1
max_{nq}=max_p+1
m
a
x
n
q
=
m
a
x
p
+
1
。同时,
p
p
p
到根的路径上所有能转移到
q
q
q
的结点都应该改为转移到
n
q
nq
n
q
(转移到
q
q
q
在
f
a
fa
f
a
树上的祖先的点不理。这样修改的点一定是链上连续的一段)。
这个构造方式的时间复杂度均摊是
O
(
n
)
O(n)
O
(
n
)
的(然而并不会证明
完结撒花。