因为这周遇到了后缀数组的模版题
所以今天顺便记一下自己对后缀数组各个地方的理解
有任何问题欢迎指出和纠正!
首先后缀就是从串中的一个开始一直到末尾
而后缀数组就是处理出一个存有每个后缀的信息
然后根据这些信息和一点LCP的性质我们可以容易求出每个后缀之间的LCP(最长公共前缀)
首先我们会处理出两个数组
一个rank数组 rank[i]表示sufi在所有后缀中的排名
一个sa数组 sa[i]表示所有后缀中排名第i位的后缀
实现的方法有两种 DC3(On)和倍增(Onlogn)
第一种实现起来比较困难 而且我也不会
所以这里讲倍增的实现方式
首先我们要做的就是给所有后缀排序
而中间在实现的过程中我们也在不断的排序
这里用的是基数排序
简单地来讲
这里的排序就是把字符串分成两部分然后按照这两部分来排序
假设串 s 由 a , x 连成 串 t 由 b , y 连成
那么 a < b时s < t , b > a时s > t
当 a = b 时 我们需要看x和y的关系
假如x = y 则 s = t ; x>y 则 s < t ;
这部分很好懂 所以我们处理的时候也是按照这种方式
我们做的就是一直合并字符串直到它成为一个后缀
并在这个过程中不断地用基数排序处理出它的排名
我们倍增的就是每次合并的这个长度 也就是logn次
每次对以每个字符开始的字符串合并 也就是n次
这里可能说得有点晦涩 不过往后看也许会理解
假设第一次排序从j=0开始
数组中的每个位置我们表示从这个位置开始的长度为2^j的串(子串)
来模拟一下 第一次排序的结果就是这样的
然后 i 位置表示的串就是以 i 起始长度为2^j的串(这里2^j是1)
把与它后一位同样表示长度为2^j的串合并
那么得到的结果就是 从 i 起始 长度为2 ^ ( j + 1 )的串
那么第一次合并就得到下面这个结果
那么想要直到新合并出的相应的每个位置表示的串的排名
我们可以根据原来两部分的排名来得出(最初的排名就是ASCII码排名)
参考图的右半部分第2次排序的rank变化
不过可能会有疑问 当 i + 2^j >n 时该怎么办?
首先可以这样理解:串s的后面是空串
那么当 i + 2^j >n 时 我们做的操作相当是把在i的串跟一个空串合并了
而空串字典序一定是最小的 所以空串的排名是0
这样这个问题就决定了
接下来有的问题可能是:
这样操作一定能让在 i 代表的以 i 为起始的串合并到成为后缀吗?
假如有 尝试证明一下
假设当前进行合并操作 每个位置 i 表示的是以 i 为起始长度为2^j的串
假设当前 i + 2 ^ j > n 而 i + 2^( j - 1) <=n
在上一次排序 i 是与 i + 2 ^ ( j - 1)合并的(这里指 i 位置代表的串 后者同样)
而 i + 2 ^ j = i + 2 ^( j - 1 ) +2^( j - 1 ) > n
所以在这次合并之前这个串就已经合并到成为后缀了
接下来进行第二次合并 那么得到的新串就是长度为2^2
每个位置代表的串对应的排名在右边我画了红线的位置
第三次合并
每个串的排名对应右边最下一行
当第1位置的串变成后缀时 所有串都变成了后缀
(简单地说 就是位置靠后的形成后缀之后 位置在前面的才能形成后缀)
而得到后缀的同时我们也处理出了每个后缀的排名
我们就可以反过来弄出sa数组
接下来就是利用rank数组和sa数组处理出H数组和height数组
H[i]记录 第 i 个后缀它前一名的LCP
hegiht[i]记录 第i名和第i-1名的LCP
不懂的话拿上面那张图的后缀举个例子
H[3]代表 baaaab 跟 b 的LCP
height[6] 代表 abaaaab 跟 ab 的LCP
这里有一个性质
假如求sufa和sufb的LCP 他们对应的排名是 l , r (假设 l < r)
那么LCP就是height数组上(l,r]的最小值
其实这是很显然的
这里我是这样理解的
这里的每个height值可以理解为一个传递的关系
假如sufa和sufb会有一个长度为k的LCP
那么从上往下一定会有大于等于k的height值传递下来
(理解不了的话记住就行了)
如何高效处理出H数组?
这里又有一个性质就是 H[i] >= H[i-1] -1
证明一下
因为LCP一定是大于等于0的 所以H[i-1]<=1时式子成立
接下来讨论H[i-1] > 1的情况
假设suf(i-1) (第i-1个 不是第i-1名)的前一名是 sufk
那么去掉suf(i-1)和sufk的第一个字母之后有suf(k+1) < sufi
(因为H[i-1] > 1 所以LCP(sufk,suf(i-1) >1 )
并且LCP( suf(k+1) , sufi ) >= H[i-1] - 1 (去掉一个首字母 )
然后注意到LCP(suf(k+1),sufi) <= LCP( sufi , suf( i前一名) )
可以推出
H[i] = LCP( sufi , suf( i前一名) ) >= LCP(suf(k+1),sufi) >= H[i-1] - 1
得到 H[i] >= H[i-1] -1
然后我们就可以利用这个性质On处理出H数组
有H数组我们可以出height数组了