字符串进阶算法 1 :exKMP、SA、SAM、后缀平衡树
字符串进阶算法 1 :exKMP、SA、SAM、后缀平衡树
exKMP / Z函数
对于字符串 的每个后缀,求其与 的最长公共前缀
要求
我们考虑当前在对 进行操作。
之前已经有一段区间 满足 ,且 最大。
-
若 ,则直接继承

-
接下来暴力匹配,只要匹配成功,此时 必然变大
SA
对字符串 的所有后缀按字典序排序。
要求
我们考虑先比较所有后缀的第一位,这可以使用桶排 。
接下来我们再通过倍增比较前2位、前4位、8位、16位、32位:

SAM
用 个节点存 的所有后缀
对于每个节点,它代表的串是长这样子的:

我们有两棵树:
- fail树:代表去掉红色段后,蓝色段所代表的节点(fail[i]:i的父亲)
- nxt树:代表在末尾加入一个字符 后,会去到哪个节点
在线构建。我们考虑加入一个字符 ,新建一个节点 :
-
连nxt,前面所有段的
nxt[x][c]=u(在nxt[x][c]为空时)寻找前面这些段的过程只需要不断跳fail即可。(即不断右移红色段)
-
如果现在出现一个
nxt[p][c] = q = != 0,我们就陷入讨论了-
如果 ,即 对于 来说,只在后面添加字母,没有延长前面的红色段,那么就可以直接
-
否则,我们需要把 前面的红色段切成两段。不妨记长的一段是 ,短的一段是 。那样子和 产生关联的都是 了(u的fail和p的next)
与 的关系则为
-

后缀平衡树
对于SA解决的问题,我们现在要带修(头加头减)
我们考虑建一棵平衡树,维护每个后缀,考虑我们从后往前,从短往长,插入每个后缀,考虑我们在每个节点上与那个节点的字符串 比较:
-
直接逐位比较法:复杂度很大
-
如果我们当前加入的是 ,而我们已经知道了 的排位了。
所以我们可以先比较第一位 ,如果相同,我们就用后缀平衡树查询 的排位与 比较
复杂度
-
我们如果让每个节点对应一个实数区间的话,我们就可以直接比较这个实数区间的大小关系
复杂度
但这样子可能需要一些不旋转的平衡树,比如替罪羊等。
如果题目全程是尾插尾删的话,我们可以倒转过来变成头插头删。
询问某个串 在 中的出现次数
相当于是多少个后缀的前缀为 。
我们直接在 后面加入一个极小字符和一个极大字符,然后查询这两个串的排位差即可。




