之前介绍过
的构建
打算做点题练习一下,但是找到
的裸题之后,根本不知道怎么用
搞
所以就来总结一下
的应用
找不同的子串的个数
方法一:用dfs处理处每个点能扩展出多少个字符串
,其实可以不用dfs,拓扑一下(按len从小到大)然后倒着做。最后sum[1]就是所有子串的个数。
方法二:
两个串的最长公共子串
这个问题的一种经典解法就是DP:
另一种解法就是后缀数组:
我们把两个串接起来,得到
数组和
数组
取
和
分别属于两个串的
的最大值即可
那么利用
要怎么解决此问题呢?
我们用
串建立
,然后用
串在后缀自动机上匹配,
因为后缀自动机中包含
串的所有子串,并且
到后缀自动机中的任意结点形成的路径都是
的合法路径,所以
串能匹配到的点到根的路径都是
的最长公共子串
那么我们只要找到所有能匹配的结点到根的最远距离即可
如果匹配到一个点后匹配不上了怎么办呢?
后缀自动机中有一个很重要的指针——
指针
每个点的
指针指向的是上一个可以接受后缀的结点,即如果当前节点可以接某个后缀那么
指针指向的结点也一定可以接受
可以将
指针指向的位置表示的状态看成该状态的一个后缀(有点像失配指针)
那么匹配不上我们就不停的跳
链,直到匹配上或者回到根节点为止
跳到一个节点后当前匹配的长度就变成了
(i指可以匹配上的节点)
例题
多个串的最长公共子串
这道题可以二分答案,用后缀数组做
(一听这个做法,就觉得比较麻烦)
那如果我们借助后缀自动机呢?
还是对第一个串建立
,不过这次匹配的时候是多个串匹配
每次匹配的时候对于后缀自动机中的每个结点维护一个值
,表示的是到达该结点所能匹配上的最大长度
(每个结点都可能是多个结点的儿子,所以从根到该点的路径长度可能是不同的)
光匹配还不够,我们需要按照拓扑序倒序(按照dis从大到小),用每个结点去更新ta的
节点
因为如果匹配到一个状态,那么实际上他
链上的所有状态都匹配上了
(
是到达每个结点的最长路径)
然后对于每个串匹配后得到的每个位置的
数组取
得到数组
,
的最大值极为答案
例题
spoj 1812 —> 题解
bzoj 2946 —> 题解
第K小子串问题
这类问题多与拓扑序有一定的关系
我们按照拓扑序倒序(按照dis从大到小)用每个点取更新
的取值,就可以得到每个位置后面有多少个子串
然后在后缀自动机上进行
,每次从字典序小的开始计算,
如果当前子树(其实不是严格的树形结构,但是我们可以形象的这样理解)的size>=k就说明第k小的子串的结尾在该子树中,
否则k-size,然后向下寻找
有点像主席树查询区间k大
例题
spoj 7258 —> 题解
bzoj 3998: [TJOI2015]弦论 —> 题解
bzoj 2882: 工艺 —> 题解
重复出现的子串问题
这类问题一般都与
集合有关系,
所谓
集合就是某个状态或者说是子串
在
中每次出现位置的右端点组成的集合,
常用来表示
集合的大小
那么 的大小怎么求呢?
是 的子集,所以我们按照 树中深度从大到小,依次将每个状态的 集合并入 状态的 集合,初始的时候只有主链上的
按照这种方式我们不仅可以求出 集合的大小,还可以求出某个子串在字符串中出现的最靠左最靠右的位置等等,在此不在赘述
这类问题静态的比较好搞,如果是动态的可能需要借助数据结构进行维护,比如下面的bzoj2555就需要用到LCT动态维护 树的形态,并维护节点的信息
例题
poj 1743: Musical Theme —> 题解
spoj 8222: NSUBSTR-Substrings
bzoj 2555: SubString —> 题解
后缀自动机与DP的结合
一般这类题中的后缀自动机是用来做预处理的,核心是DP
所以说:浔阳DP无音乐,终岁不闻AC声
例题
bzoj 4180: 字符串计数 —> 题解
bzoj 4032: [HEOI2015]最短不公共子串 —> 题解
bzoj 2806: [Ctsc2012]Cheat —> 题解
bzoj 3238: [Ahoi2013]差异
广义后缀自动机
一般有两种形式
一种是对
建立广义的后缀自动机
另一种是对多个独立的串建立广义的后缀自动机
对于trie树:我们在只有一个串的时候
就是直接指向前一个字符的位置,因为我们现在是树结构,所以
指向的应该是该点在
树中父节点的位置,剩下的建立过程与普通的后缀自动机相同
对于多个串:每次加完一个串就把
回到
然后自然加入即可
例题
bzoj 3926: [Zjoi2015]诸神眷顾的幻想乡 —> 题解
bzoj 2780: [Spoj]8093 Sevenk Love Oimaster —> 题解
bzoj 3277: 串
bzoj 3473: 字符串
集合与 树
集合与
树是后缀自动机最常用也是最好用的两个东西
集合一般用来处理计数问题,两者相辅相成不可分离
有一个比较有用的性质:两个串的最长公共后缀,位于这两个串对应状态在 树的最近公共祖先上
例题
bzoj 4516: [Sdoi2016]生成魔咒
bzoj 4566: [Haoi2016]找相同字符
bzoj 1396: 识别子串