个人感觉挺不错的
转自:https://blog.csdn.net/Akak__ii/article/details/51278533
因为jzoj不开放注册我就把一些题目换了一下
前言
之前学了后缀数组,这真是一个神奇的东西。早就想总结一些SA的应用,但一直没时间,现在终于抽出空来写一下自己的心得。
本文只讨论后缀数组的一些应用,不一定全面,仅供参考
还不会后缀数组的同学请自学后再看本文,这里不再赘述后缀数组的基础实现
推荐一篇博客,里面对于基础的后缀数组总结的比较到位,YxuanwKeith大神的五分钟搞懂后缀数组! 后缀数组解析以及应用
请不要吐槽标题,不知道你五分钟搞不搞得懂,反正我搞不懂 = = 。
下面进入正题。
后缀数组相关应用
单个字符串相关问题
比较常见的思路是先构造后缀数组,然后求Height数组,用这两个来求解。
重复子串问题
可重叠的最长重复子串问题
因为可重复,所以这类问题比较简单。只需要求Height数组的最大值。因为Height[i]表示排名为i和i−1的后缀的LCP,所以这个LCP一定是重复的,只需要求个最大值就是最长的。时间复杂度是线性的。
不可重叠的最长重复子串问题
有了不可重叠的限制稍微复杂一点,要用到Height数组的性质。先二分答案,将问题转化为判定性问题。假设我们二分的长度为k,那么答案的两个串它们在SA中的之间的Height值都≥k(想一想为什么?),所以我们把连续一段Height≥k的后缀划分成一段,如果有某一段满足段中最大的SA值与最小值之差大于等于k,那么当前解就是可行的,因为满足这一条件的串一定不重叠。
注意:这种分段的思想在后缀数组相关问题中很常用
可重叠的 k 次最长重复子串
例:(poj 3261 Milk Patterns )给定一个字符串,求至少出现 k 次的最长重复子串。
做法和上面的差不多,还是先二分,但是条件改变了,不是不重叠,而是出现至少k次。只需判断当前段内是否出现k个后缀即可。
子串计数问题
重复出现子串计数问题
例:(JZOJ1598. 文件修复)求一个字符串中有多少个至少出现两次的子串
这是比较简单的SA题,设每个后缀Rank为i,其最多能贡献**Height[i]−Height[i−1]**个不同重复子串。
∴Ans=∑max(Height[i]−Height[i−1],0)
ps:这题我找不到,看看就行
不相同子串计数问题
例:(Distinct Substrings)给定一个字符串,求不相同的子串的个数。
和上面思路大相径庭。每个后缀kk会产生n−SA[k]+1个前缀,但是会重复计数,所以要减去前面相同的前缀,最后就是 n−sa[k]+1−height[k] 个子串。
字典序第K子串问题
例:(Boring String Problem )给出一个字符串S,问该字符串的所有不同的子串中,按字典序排第K的字串,如果有多个则输出最左边的。
应该勉强算计数问题吧。。。其实就是不相同子串个数的扩展,算出每个后缀贡献的不同子串个数,在二分找出最终子串位置(必然是某个后缀的前缀)。
最长回文串
把这个串反过来加到原来串后面,并用一个无关的字符连接(比如’#’),问题就变成求这个新串的2个最长公共前缀问题,注意分奇偶讨论.
连续重复子串问题
例:(pku 2406)给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的,
求 R 的最大值。
比较简单的重复子串问题。枚举串S长度k,如果Rank[1]和Rank[k+1]的Height=|L|−k那么当前答案合法。
ps:倍增超时了,必须得用DC3,不过不会就是了.(正解其实是kmp?)
重复次数最多的连续重复子串问题
例:(pku 3693)给定一个字符串L,求重复次数最多的连续重复子串,且字典序最小。
还是枚举子串长度k,看这个长度对应每个位置(即L[0],L[k],L[k∗2]…L[0],L[k],L[k∗2]…)之间的LCP是否等于k最长能扩到哪里,就是重复出现次数。
多个字符串相关问题
常用做法是将多个串连在一起,并且中间插入不同且没出现过的字符隔开(想一想为什么?)。但是这种题比较多变,不太好总结,只能简述一些例子。比如最长回文串
一个字符串在所有字符串中出现次数问题
例:(JZOJ3258. 【TJOI2013】单词)给定N个字符串,求每个字符串在所有字符串中出现的次数。
对于当前字符串S,设其在总串中起始位置为ST[i],那么我们在Height上二分区间[1,Rank[ ST[i]] ] , [ Rank[ST[i]]+1 , n ]内满足Height≥|S|,这之间的后缀数量就是答案。
字符串不同种连续子串问题**
例:(JZOJ4473. 生成魔咒)给定n个操作,每个操作在字符串S尾插入一个字符,求当前操作后共有多少不同种连续子串。
仔细分析,这题要减去前面的操作对当前影响(即重复的连续子串)。这个应该算是“前缀数组”吧。具体来说就是将字符串翻转后求一边SA,此时所得就是原串的前缀数组。然后在线段树维护一下。
字符要求输出字典序或者是有某些要求但是又比较棘手的时候可以考虑直接暴力枚举答案.
后记
这篇博客是在复习之余码的,由于水平有限,难免有疏漏之处,欢迎批评和补充。后续(或许)会有例题及应用的补充。
参考资料:罗穗骞《后缀数组——处理字符串的有力工具》
以上.