【学习笔记】后缀数组 SA

  人太蠢了还是不行,看个基数排序看出了脑震荡。

  一些规定:

    x位后缀:一个字符串去掉前x个字符串得出的字符串

  •   后缀排序

  后缀数组其实就是把一个字符串的所有后缀排序之后整出来的东西,例如

aabbcca

  排序后

aabbcca
abbcca
a
bbcca
bcca
ca
cca

  用于实现后缀排序的主要是倍增法,可以在O(nlogn)的时间复杂度内完成基数排序。而倍增算法也就像所有字符串算法的共性那样:利用已经完成计算的信息优化后一步的信息计算。

  就实现而言,我们步骤大体如下:

  1.   记录下所有后缀第一位的位置,作为它们的编号
  2.   先将所有后缀按照第一位的大小排序
  3.   把排好序的所有后缀与它编号+i(从1开始)的后缀“综合”一下,快速得出后缀们前2 ^ i次方位的排序。
  4.   把i * 2,重复第3、4步。

  那么,怎么把后缀“综合”一下呢?我们举个。

  假设我们已经把字符串acabaad按照第一位的大小排好了序

acabaad
abaad
aad
ad
baad
cabaad
d

  设编号为i的字符串Si的排名为rki,那么可知,Si+1为Si的“一位后缀”。若Si与Sj第一位比较出的顺序相同,我们可以借rki+1与rkj+1对比出Si与Sj前两位的顺序,依次类推,若我们知道Si前len位的排序,我们可以借Si+len求出2 * len的排序。这样,逐位比较进行排序的次数就下降了许多次。

  考虑到在排序时,我们事实上只是综合了rki与rki+len进行排序,我们事实上有且仅有一些“两位数”rkirki+len要排序,可以选择优秀的基数排序把单次排序复杂度缩减至O(n)。

  •   基数排序

  这一部分乍一看很简单理解,事实上,要以正确而快速的复杂度实现又是另一码事了。

  思想相当简单,对于一个两位数,先将第二位进行桶排,再将桶排后的顺序用第一位桶排就可以保证两位数的排名了。但实现是一件相当有挑战性的事。

  •   height数组

即lcp(sa[i - 1],sa[i]),height[i]也就是第i个字符串与在它排名前那个字符串的最长公共前缀。

关于height[rk[i]] >= height[rk[i - 1]] - 1的证明:

  设位置为i - 1的后缀为s1,在它排名前的一个后缀为s2,设位置为i的后缀为s3,在它排名前的一个后缀为s4。若两字符串首字母不同,则height[rk[i - 1]] = 0,可得结果。

  若两字符串首字母相同,则两字符串的一位后缀的先后顺序同这两个字符串的先后顺序一样,也就是说s2的一位后缀在s3前,s2的一位后缀的排名<=s4。可知s4与s3的lcp要大于等于s4与s2的一位后缀的lcp。

  而s1的一位后缀与s2的一位后缀的lcp明显是height[rk[i - 1]] - 1,故结果成立。

(证明思想剽窃自自为风月马前卒的博客) 

口胡的证明,看不懂正常(我自己也看不懂)。

  有了这个,我们就可以从height[rk[i - 1]]快速推出height[rk[i]],复杂度为O(n)。

原文地址:https://www.cnblogs.com/Illusions/p/13885135.html