后缀数组

对于一个字符串(S),我们记后缀 (Suf_i) 为从 (i) 开始一直到末尾所形成的子串((S_iS_{i+1}...S_{len}))。

现在我们要对所有后缀按照字典序排序。记 (sa_i) 表示排名第 (i) 的后缀的起始位置,(x[i]) 表示 (Suf_i) 的排名。

考虑使用一种倍增的方法排序:一开始我们提取每个后缀的长度为 (1) 的字符进行排序;然后将每个后缀的第 (1) 个字符作为第一关键字,第 (2) 个字符作为第二关键字,对这些长度为 (2) 的字符串进行排序;然后将每个后缀的前 (2) 个字符作为第一关键字,第 (3) 和第 (4) 个字符作为第二关键字,将长度为 (4) 的字符串进行排序……

由于我们对每个后缀长度为 (2n) 的字符串排序时,长度为 (n) 的字符串的排名已经确定,所以这个算法的正确性是显然的。

如果用 (sort) 直接排序,加上倍增的一个 (log) 总复杂度是 (O(nlog^2n)) 的,考虑优化:由于只有两个关键字且值域范围较小,我们可以使用基数排序,一种 $O(n) $ 的排序方式。这个不太好讲解,最好靠自己理解,实在看不懂可以背。代码中 (y) 数组的含义是第二关键字排名第 (i) 的第一关键字的位置为 (y_i) (这好像是唯一能提供的帮助了)。

好了现在通过慢慢理解你现在求出了 (sa) 数组和 (x) 数组了,我们现在用他们来搞点事情(毕竟不可能让你排个序就完了)。

(lcp(i,j))(Suf_i)(Suf_j) 的最长公共前缀,(height_i) 表示 (lcp(sa_i,sa_{i-1}))

假设我们现在已经拥有了 (height) 数组,考虑如何快速求 (lcp) :容易发现(假设(x_j>x_i)

[lcp(i,j)=min(height_{x[i]+1},height_{x[i]+2},...,height_{x[j]} ) ]

比较通俗的理解就是一个排名为 (i) 的字符串,相对于与排名为 (j(j>i+1)) 的串的 (lcp) 来说,与排名为 (i+1) 的串的 (lcp) 肯定不会更短。

有了这个性质以后,我们发现求两个后缀的 (lcp) 就相当于求一个区间最小值,一个 (ST) 表就可以解决.

好了现在看怎么求 (height) 数组:

(h_i=height_{x[i]}),人话的意思就是 (h_i) 表示 (Suf_i) 与它排名前一位的后缀的 (lcp)

可以发现一个关系:

[h_igeq h_{i-1}-1 ]

我们首先发现 (Suf_i)(Suf_{sa[x[i-1]-1]+1 })(lcp) 一定为 (h_{i-1}-1)(前提是 (h_{i-1} eq 0)),因为 (h_igeq h_{i-1}-1) 这种关系我们可以直接 (O(n)) 推出 (h) 数组,在还原一下就能回到 (height) 数组了((height_i=h_{sa[i]}))。

  • 例题:
  1. luogu P3809 【模板】后缀排序 题解
  2. luogu P3181 [HAOI2016]找相同字符 题解
  3. luogu P2852 [USACO06DEC]Milk Patterns G 题解
  4. luogu P5546 [POI2000]公共串 题解
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
原文地址:https://www.cnblogs.com/With-penguin/p/13155639.html