后缀数组的实现

因为这周遇到了后缀数组的模版题
所以今天顺便记一下自己对后缀数组各个地方的理解

有任何问题欢迎指出和纠正!

首先后缀就是从串中的一个开始一直到末尾
而后缀数组就是处理出一个存有每个后缀的信息
然后根据这些信息和一点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数组了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值