[后缀数组复习笔记]

首先推荐一篇写的非常好的Blog,本文中部分内容也会选自该博客。

个人认为后缀数组的核心内容其实就是对\(2^x\)的字符串按照\(2^{x - 1}\)求出的第一第二关键字进行排序.然后进而一步一步对数组进行排序.

所以要用到基数排序:

基数排序在后缀数组中可以在\(O(n)\)的时间内对一个二元组\((p,q)\)进行排序,其中\(p\)是第一关键字,\(q\)是第二关键字

为了方便,本文用\(i\)代表\(i\)开头的后缀

首先我们要明确用到的数组的含义(非常重要!)

\(sa_i\):排名为\(i\)的后缀的位置

\(rk_i\):后缀\(i\)的排名

\(x_i\)后缀\(i\)的第一关键字

\(y_i\)基数排序的第二关键字,表示第二关键字为排名为\(i\)的后缀的位置

\(c_i\)\(i\)这个元素的出现次数,用于基数排序

\(s\),需要进行排序的字符串

利用倍增的思想。

因为上来当倍增长度为\(1\)时,没有第二关键字

第一关键字就是其字符大小.

倍增过程,\(m\)是其可以区分的后缀数量,\(w\)为当前的倍增长度.就是我上文说的\(2^{x - 1}\)

这是后缀数组的核心之一.求出后缀\(i\)的第二关键字的大小

第一行:如果该后缀的长度不大于\(2^{x - 1}\),第二关键字对应的串就一定是空串.就没有排名一说了,直接顺序排下来.

第二行:如果当前为排名为\(i\)的后缀(此时的\(sa_i\)记录的长度还是\(2^{x - 1 }\),)他是\(sa_i - w\)的第二关键字,而我们是枚举的排名,所以\(sa_i - w\)这个后缀如果存在,他的第二关键字的排名一定是相应的排名.

基数排序不会就背吧QAQ(只需要理解第一第二关键字就好了)

基数排序完成后,\(y\)数组没用了,我们利用得到的\(sa\)求出新的\(x_i\)

但是\(x_i\)可能会重复,所以要利用上一轮的\(x_i\),这就是将\(x\)数组复制给\(y\)而不直接丢掉的原因.

如果\(sa_i\)\(sa_{i - 1}\)的第一关键字排名相同且向后倍增\(2^{x - 1}\)也相同

那么\(sa_i\)\(sa_{i - 1}\)长度为\(2^x\)的后缀排名也是相同的.

height

上面得出的东西,都是为了\(height\)数组

我们引入一些概念

\(lcp(x,y)\):字符串\(x\)与字符串\(y\)的最长公共前缀,在这里\(x\)号后缀与\(y\)号后缀的最长公共前缀

\(height[i]\)\(lcp(sa[i],sa[i−1])\),即排名为\(i\)的后缀与排名为\(i−1\)的后缀的最长公共前缀

\(H[i]\)\(height[rak[i]]\),第\(i\)号后缀与它前一名的后缀的最长公共前缀

那么有一个结论

\(H[i] >= H[i - 1] - 1\)

\(height[rk[i]] >= height[rk[i - 1]] - 1\)

利用这个性质,我们便可以\(O(n)\)得出\(height\)

到这里后缀数组就完成了

但是关于\(height\)的应用是解大多数字符串题目的关键。掌握扎实

视题目而定,灵活应用