算法合集之《后缀数组——处理字符串的有力工具》

后缀数组:

sa[i]:表示排名第i个的首字母位置
Rank[i]:第i个数的排名
Height[i]:sa[i]和sa[i-1]的最长公共前缀


suffix(j) 和suffix(k) 的最长公共前缀为height[rank[j]+1],
height[rank[j]+2], height[rank[j]+3], … ,height[rank[k]]中的最小值。

由于是求区间最小值,所以还可以往上面套一个RMQ


1.不可重叠最长重复子串(pku1743)

给你一串数字,求它们最长的重复(公差相同)子序列,且两个子序列不相交

我们可以向二分枚举ans长度,如果能找到两个height>=ans,而且通过sa判断两个的间距>=ans,则说明这个答案合适。

解题报告


2.可重叠的k 次最长重复子串(pku3261)

最开始想到是二分枚举ans长度,从头到尾找出height>=ans的总数,但是这要容易出现问题,无法保证是同一个串重复出现的。所以应该分组讨论,即连续>=ans的长度(可以保证它们的前缀是相同的)。

解题报告


3.不相同的子串的个数(spoj694,spoj705)

可以转换成求相同子串的个数,而相同子串的个数可以转换成公共前缀的个数

->len*(len+1)/2-sum(height[])

解题报告


4.最长回文子串(ural1297)

分两种情况,一是回文子串的长度为奇数,二是长度为偶数。两种情况都可以转化为求一个后缀和一个反过来写的后缀的最长公共前缀。具体的做法是:将整个字符串反过来写在原字符串后面,中间用一个特殊的字符隔开。这样就把问题变为了求这个新的字符串的某两个后缀的最长公共前缀。

解题报告


5.连续重复子串(pku2406)

需要判断Rank[0]和Rank[k]是否为n-k
(因为如果相等,0~k = k+1~2*k+1 .... 递推下去 整个串是0~k的子串不断重复得到)

所以我们可以先处理出每个到rank[0]的位置(从rank[0]的位置往两边扫一遍),然后枚举判断一下即可

解题报告


6.重复次数最多的连续重复子串(spoj687,pku3693)

首先,枚举l(用来重复的长度),判断suff[i],suff[i+l],如果公共前缀k%l != 0,则说明这个长度不合适,修改后再进行判断。
于是考虑k%l,可以看成两个串之间多了k%l个字符,但可以看成前面少了m = l-k%l,所以把两个串都往前移动m个字符,于是成了求 l-i-m,l-m的情况,如果还是不合适->舍弃
然后记录最大次数cnt和符合条件的所有解a[]最后进行判断,因为要求字典序最小,所以从sa[1]开始判断

解题报告


7.最长公共子串(pku2774)

相当于求两个字符串所有后缀的最长公共前缀,所以我们可以把两个字符串用一个特殊字符间隔连起来然后找出最大的height,同时通过sa数组判断一下它们是否

解题报告


8.长度不小于k 的公共子串的个数(pku3415)

从头到尾枚举height,如果当前是属于A串,则加上前面所有属于B串的height-k+1.对于B串同理.

两个串之间的公共前缀是它们之间所有的最小值,所以用栈维护一下,保证栈里是单调递增的,这样对于新增的串只需要处理其中height大于它的一部分即可.

解题报告


9.不小于k 个字符串中的最长子串(pku3294)

求的是最长公共子串,所以考虑 二分答案len+判断
因为要判断是否为x个串共享所以对height进行分组,即height数组中各个连续≥len
的集合,然后对每个组进行判断,看书否能找到x+1个不同的来源。
满足条件就记录 子串的起始位置和长度

1.串之间的间隔符号不能相同
2.因为有100个串,所以已经占据了0-99,所以字符串的信息转换成int的时候
必需是从100开始

解题报告


10.每个字符串至少出现两次且不重叠的最长子串(spoj220)

因为是求的最长子串,所以考虑二分长度len
然后我们需要对其进行判断,对于每一个连续大于等于len的height[](分组讨论)
记录各个串中的情况,因为要判断不是重叠的,所以对于每个串,我们记录
它满足height>=len的最大最小位置
如果所有串的max-min >= len 则说明存在长度为len的子串在每个串都有出现两次且不重叠

解题报告

hdu5558

从[1,n]对于每个i,求suff[j](j < i)与suff[i]的最长公共前缀,如果有多个,取最小的那个

我们可以通过后缀数组先求出,如果i-1和i,i和i+1都有公共前缀,

那么i-1和i+1也有公共前缀,所以可以先处理出每个i的左右界限。然后对于i左右扫描一下即可

解题报告


hdu 3948 后缀数组

给你一个字符串,问其中不同回文子串的个数

 首先height数组可以知道两个回文子串的关系,所以枚举height.

通过ta记录当前位置与上一个回文串的公共前缀,当遇到下个回文串就能知道两个回文串的公共部分.即需要减去的重复部分
ta = min(ta,height[i]);
而且枚举height不知道当前回文串是否处理过,所以用vis记录一下
 
hdu 5008 查找字典序第k小的子串
给你一个字符串,每次寻找字典序第k小的子串(不同的)的开始,结尾坐标。
首先可以知道height本身就是按字典序来弄的,对于sa[i-1]和sa[i]而言只要减去
它们的公共部分即height[i],就是不同的子串.
可以借此处理出来[1,n]即到字典序第i大的后缀时总共有多少个不同的子串.然后
利用二分查找就能找到位置
还要注意就是当有多个的时候输出最小的位置,可以判断当前子串是否是公共前缀的
一部分.往后枚举找出最小的位置即可
原文地址:https://www.cnblogs.com/Przz/p/5409570.html