【学习笔记】后缀数组
对算法本身的部分理解:
本部分可以认为是读过一位大佬的后缀数组博客的感受,加上对于某些地方我感觉大佬解释的不是很明白,所以还有一部分我自己对这一部分的理解与解释,当然只是部分我认为不是很懂的内容,只是一部分,更多还要去看这位大佬的博客。
第一行:
这就相当于对于一个字符串,其没有后 \(w\) 位,既然没有后 \(w\) 位,那就应该是排在前面
第二行:
首先不用管所谓的 \(sa[i] > w\),这只是个特判没有任何意义,其实也就是为了保证存在而已,也暂时不用考虑第一条语句所提前处理出来的一些数据。
我们现在必须搞清楚我们要求什么,求的也就是 \(tp\) 数组,其中 \(tp[i]\) 代表长度为 $2\times w $ 的串中,只看后 \(w\) 个元素排名为 \(i\) 的串的起始位置
对于 \(sa[i] - w\) 就是让原本的长度为 \(w\) 的后缀中排名为 \(i\) 的那个的起始位置左移 \(w\) 位,那么是不是也就相当于 \(sa[i]-w\) 就是代表着左右长度为 \(2 \times w\) 的后缀中,按后 \(w\) 个元素排序的第 \(i\) 个串的起始位置。我们考虑 \(sa[i]\) 代表着长度为 \(w\) 的后缀中排名为 \(i\) 的那个元素的排名,那么对于长度为 \(2\times w\) 的串来说,\(sa[i]\) 其实就相当于某一个长度为 \(2\times w\) 的串的后缀,那么这个串很明显就是 \(sa[i] - w\) 为开始的串。注意 \(sa[i]\) 里存的也是起始位置。
因为我们的 \(i\) 从小到大枚举,所以我们直接计数器累加即可
我们考虑此时 \(sa[i]\) 就是长度为 \(w\) 的串中排名为 \(i\) 的串起始位置,\(tp[i]\) 就是所有长度为 \(2\times w\) 的串中后按 \(w\) 个元素排序,排名为 \(i\) 的串的起始位置, \(rk[i]\) 就是所有长度为 \(w\) 的串中以 \(i\) 为开头的串排第几。我们的基数排序就是以 \(rk\) 为第一关键字以 \(tp\) 为第二关键字的排序,此时这两个关键字都处理好了,所以直接排序得到新的 \(sa\) 数组即可
我们首先必须明确 \(tax[i]\) 代表按前 \(w\) 个元素排名,排名小于等于 \(i\) 的串的个数,也就是代表着按前 \(w\) 个元素排名,排名为 \(i\) 的元素的排名加排名为 \(i\) 的串的个数
前三行非常好理解,就是为了得到 \(tax\) 数组,关键在与最后一行非常难以理解。
我们先一点点地看
\(rak[tp[i]]\) 代表的含义就是长度为 \(2\times w\) 的串中,后 \(w\) 个元素排名为 \(i\) 的那个位置的前 \(w\) 个元素的排名
\(tax[rak[tp[i]]]\) 也就是代表着长度为 \(2\times w\) 的串中,后 \(w\) 个元素排名为 \(i\) 的那个位置的前 \(w\) 个元素的排名,注意这里的最后的排名是指被分到的排名
$tax[rak[tp[i]]] -- $ 关于这个自减的理解,我们考虑就是对于按第一关键字也就是前 \(w\) 个元素排名相同的点,因为我们的第二关键字不一定相同所以我们的最终他们虽然第一关键字一定一样但是第二关键字不一定一样,因此被分到的排名也不一定相同(可能有点绕),注意我们的循环顺序,也就是枚举的顺序,我们是按第二关键字从大到小去枚举,也就意味着当这个长度为 $2\times w $ 的字串的第一关键字也就是前 \(w\) 个元素排名相同时,我们第二关键字也就是按后 \(w\) 个元素的排名越大被分到的最终排名也就越大,也就符合了我们按双关键字排序的目的。
\(sa[tax[rak[tp[i]]]−−] = tp[i]\) 也就相当于排名为 \(tax[rak[tp[i]]]−−\) 的串的起始位置也就是 \(tp[i]\) 这个串的起始位置,也就是 \(tp[i]\)
这里就是对 \(rk\) 数组的更新
对于这里的 \(tp[i]\) 代表长度为 \(w\) 的串里起始位置为 \(i\) 的串的排名,也就是我们之前的 \(rk\) 数组, \(sa[i]\) 代表长度为 \(2\times w\) 的串里,排名为 \(i\) 的串的起始位置
我们这里其实就是在判断排名为 \(i\) 的串和排名为 \(i-1\) 的串的排名是否相同,第一个判断也就相当于他们前 \(w\) 个元素是相同的,第二个判断也就相当于他们的后 \(w\) 个元素是相同的,所以如果两个全部满足,也就意味着 \(2\times w\) 个元素都是相同的,也就意味着 \(i\) 与 \(i-1\) 的排名相同
关于 \(height\) 数组的更新:
对于 \(height\) 数组某大佬证明了一个定理:
\(H[i]\ge H[i-1] - 1\)
我们对 \(height\) 数组的定义,排名为 \(i\) 的后缀与排名为 \(i-1\) 的后缀的最长公共前缀的长度,即 \(height[i]=lcp(sa[i],sa[i-1])\) 。
我们定义 \(H[i] = height[rak[i]] = lcp(sa[rak[i]],sa[rak[i]-1])=lcp(i,sa[rak[i]-1])\) 也就是第 \(i\) 号后缀与排名在第 \(i\) 号后缀前一个的后缀的最长公共前缀的长度,这里的所谓的前一个后缀的起始位置也就是 \(sa[rak[i] - 1]\) ,对于 \(H\) 数组直接根据定义加下界去算就可以了,算完了更新 \(height[rak[i]]\) 即可