这是本文档旧的修订版!
后缀数组有两个数组 $sa$ 和 $rk$ 。
$sa[i]$ 表示将所有后缀排序后第 $i$ 小的后缀的编号, $rk[i]$ 表示后缀 $i$ 的排名。
所以显然 $sa[rk[i]]=rk[sa[i]]=i$ 。
显然暴力排序求这俩数组的做法是 $n^{2}logn$ 的。
有一个倍增算法,是利用上一次前和后两个段的排序名次作为第一第二关键字,进行新的一轮排序,这样需要排序 $logn$ 次,每次如果是 $sort$ 排序,单次复杂度是 $nlogn$ 次,这样是 $nlog^{2}n$ 的。用基数排序可以 $O(n)$ 排出结果,这样复杂度就是 $O(nlogn)$ 的。
给一个字符串,长度不超过 $10^{5}$ 。将这个字符串循环放置,让求将这些循环出来的字符串按照字典序从小到大排序,并取最后一个字符接成一个新的字符串,让求这个新的字符串是什么。
设原来的长度为 $len$ 。既然是循环放置,我们自然考虑倍增。那么比较这些字符串的大小,只需要取出长度大于原长度的所有后缀,分别取他们的前 $len$ 项,但是注意到,我们比较他们的大小,相当于比较这些后缀的大小。比如现在有两个题目给的串 $s1,s2$ , 不妨设 $s1>s2$ ,然后加上后缀,我们必然有 $s11>s22$ ,因为这两个字符串在 $len$ 长度的时候就已经比出来了 $s1>s2$ 。反之一样,如果 $s1=s2$ ,那么我们无所谓哪个放在前面,因为最后一位是一样的,所以就是倍增之后求后缀数组,存一下位置之后按 $rk$ 排序,之后取走每个位置的最后一个字符就可以了。
其实最小移动位置也是可以这么求的,只不过复杂度变成了 $O(nlogn)$ 的。
2.如何在线在字符串中找出模式串
设主串是 $T$ ,模式串是 $S$ ,我们先构造出主串的后缀数组,注意到如果模式串出现在主串中,一定是作为这个主串某些后缀的前缀,这样我们可以二分 $S$ ,比较子串 $S$ 和当前后缀的时间复杂度是 $O(|S|)$ 的,于是这个复杂度就是 $|S|log|T|$ 的。如果要求出现的次数,可以在后面补一个很大的字符,然后两次二分做差就可以了,输出位置取两次二分出来的排名,然后取左闭右开区间这些排名的 $sa$ 数组位置,输出就好了。因为后缀平衡树是可以实现后缀数组的操作的,所以这个东西后缀平衡树也可以,已经在那篇里写过了。
给定一个长度为 $n$ 的字符串,要求构造一个新的字符串,构造规则是:每次取出原字符串的首或尾巴,排到新字符串的尾巴,然后要新的字符串字典序最小
通过大眼观察法就可以知道,其实我们每次判断的依据无非是剩下字符串是从头到尾字典序小还是从尾到头字典序小,就这么贪心地取,因为这样从字典序小的那边取一定不会比从字典序大的那边取差,所以是没问题的。直接硬比是 $O(n^{2})$ 的。这时就有一种想法,顺着求后缀数组并且逆着求后缀数组,但是这样怎么比较哪个小呢,这么比依然是 $O(n^{2})$ 的。哦,那就逆着接到后面一起求后缀数组就好了啊,是我太蠢了…这时和上面的类似,我们每次比较的是想同长度的串,所以后缀之间的比较就和那两个串的比较结果是一样的(相同的串取哪个都无所谓)。最后就设两个队头,直接比较他们的 $rk$ 数组值就好了,小的放在队里面,最后把队输出就行了。