写在前面
首先要知道后缀数组的作用:将字符串的每一个后缀按照字典序进行排序,当然我们还可以进行其他的操作们可以求解相邻两个后缀之间最长的公共前缀的长度(LCP问题)
后缀数组
首先我们要明白基数排序,如果不明白的话可以看一下我的另一篇博客:基数排序的实现
当我们明白了基数排序之后,我们就可以看一下后缀数组的模板了:
//不怕别人比你聪明,就怕别人比你聪明还比你努力
#include
#include
#include
#include
#include
#include
#include
#include
#include
我们这样就可以得到后缀的基本排序,我们后面看一下后缀数组的基本应用
后缀数组的基本应用
后缀数组中有很多应用,这里我们就先说一个应用:最长公共前缀,其余的应用我会在遇到一些题中说。
我们这里引入一些辅助数组:rank[] :表示从第i个字符开始的后缀排名第几,这里和上面的SA数组的表示正好是相反的。
height[]:我们定义height[i] = suffix(sa[i-1]) 和 suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。这里suffix(i)表示从i开始到结尾这个后缀
那么对于j和k,我们设rank[j] < rank[j],则具有以下性质:
suffix(j) 和 suffix(k)的最长公共前缀为 height[rank[j] + 1],height[rank[j]+2]......height[rank[k]]中的最小值
注:那么我们可以得到两个前缀的最长公共前缀需要求解一个区间的最小值,那么这里就是我们的RMQ问题,可以看一下另一博客:RMQ问题原理及实现
但是我们还是要看一下我们应该怎么求解height数组,这里我们有要用到一个辅助数组,以及这个辅助数组的一个性质。
h[]:定义h[i] = height[rank[i]],也就是suffix(i)和它前一名的后缀的最长公共前缀
h数组有以下性质: h[i] >= h[i-1]+1
我们按照h[1],h[2],h[3]......的顺序求解,并且利用上面的性质,我们可以将求解height数数组的时间复杂度降为O(n)
我们首先计算出rank数组
for(int i =0 ;i < len;i ++) rank[SA[i]] = i;
然后我们按照h[1],h[2],h[3]...的顺序计算height数组
void get_height()
{
for(int i =0 ;i < len;i ++) rank[SA[i]] = i;
int k = 0;
for (int i = 0; i < len; i++)
{
if (rank[i] == 0) {height[0] = 0; continue;} // 当排名为0时,因为他前面没有字符串,所以这里就应该定为0
if (k) k--; // 从 k - 1 开始推,这里我们就利用了上面h数组的性质
int j = SA[rank[i] - 1]; //得到上一名的开始位置
while (str[i + k] == str[j + k] && i + k < len && j + k < len) k++;//相同的话,我们就一直向后加
height[rank[i]] = k; //更新height数值
}
}
最终我们得到完整的程序
//不怕别人比你聪明,就怕别人比你聪明还比你努力
#include
#include
#include
#include
#include
#include
#include
#include
#include
后记
这个算法真的是看了三四天才看明白,并且上面整理的也是在自己刚看明白的时候整理的,如果这里面有写的很烂的地方或者有错误的地方,请在下面评论,我会及时进行更改,并且后面对后缀数组有了更深的理解,我会及时过来进行修正。谢谢!
参考文献
知乎后缀数组(Suffix Array):https://zhuanlan.zhihu.com/p/21283102
后缀数组详解+模板:https://www.cnblogs.com/thmyl/p/6296648.html