后缀数组复习笔记
[后缀数组复习笔记]
首先推荐一篇写的非常好的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\)的应用是解大多数字符串题目的关键。掌握扎实
视题目而定,灵活应用