后缀数组
对于一个字符串\(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\))
比较通俗的理解就是一个排名为 \(i\) 的字符串,相对于与排名为 \(j(j>i+1)\) 的串的 \(lcp\) 来说,与排名为 \(i+1\) 的串的 \(lcp\) 肯定不会更短。
有了这个性质以后,我们发现求两个后缀的 \(lcp\) 就相当于求一个区间最小值,一个 \(ST\) 表就可以解决.
好了现在看怎么求 \(height\) 数组:
设 \(h_i=height_{x[i]}\),人话的意思就是 \(h_i\) 表示 \(Suf_i\) 与它排名前一位的后缀的 \(lcp\) 。
可以发现一个关系:
我们首先发现 \(Suf_i\) 与 \(Suf_{sa[x[i-1]-1]+1 }\) 的 \(lcp\) 一定为 \(h_{i-1}-1\)(前提是 \(h_{i-1}\neq 0\)),因为 \(h_i\geq h_{i-1}-1\) 这种关系我们可以直接 \(O(n)\) 推出 \(h\) 数组,在还原一下就能回到 \(height\) 数组了(\(height_i=h_{sa[i]}\))。
- 例题: