后缀数组

后缀数组

构造后缀数组

后缀数组是将字符串所有的后缀存储到一个数组中,根据各个后缀的字典序排序后的结果数组。

\(sa[i]\) 表示排名为 \(i\) 的后缀编号,而 \(rk[i]\) 则表示编号为 \(i\) 的后缀的排名。

如何优化呢,考虑倍增,类似于 st 表的构建过程,转化为若干次双关键字排序。

img

此时我们的算法是 \(O(nlog^2n)\) 的,很猪猪,但已经能过洛谷板子题了。

void init()
{
    rep(i,1,n) sa[i] = i, rk[i] = s[i];
    for(int w = 1;w <= n;w <<= 1)
    {
        auto rp = [&](int x) { return std::make_pair(rk[x], rk[x + w]); };
        std::sort(sa+1,sa+1+n,[&](int x,int y){return rp(x) < rp(y);});
        int p = 0;
        rep(i,1,n) tp[sa[i]] = rp(sa[i - 1]) == rp(sa[i]) ? p : ++p;
        std::copy(tp + 1, tp + n + 1, rk + 1);
    }
}

回归到排序本身,发现我们倍增每一层的排序都是双关键字排序,可以使用基数排序进行优化。

还可以发现,对于第二关键字,我们并不需要排序,只需要把超出字符串范围的编号放到头上,剩下的按原顺序放回去即可。

void qs()
{
	rep(i,1,m) a[i] = 0;
	rep(i,1,n) ++ a[rk[i]];
    rep(i,1,m) a[i] += a[i - 1];
	per(i,n,1) sa[a[rk[tp[i]]] --] = tp[i];
}

void init()
{
    rep(i, 1, n) {rk[i] = c[i] - 'a' + 1, tp[i] = i;}
    qs();
	for(int w = 1, p = 0; p != n && w <= n; m = p, p = 0, w <<= 1)
    {
		rep(i, n - w + 1, n) tp[++p] = i;
		rep(i, 1, n) if(sa[i] > w) tp[++p] = sa[i] - w;
		qs(), std::swap(tp, rk), rk[sa[1]] = p = 1;
		rep(i, 2, n) rk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
	}
}

\(O(nlogn)\) 并不是后缀排序的理论最优复杂度,还可以使用 SA-IS 或者 DC3 做到线性复杂度,由于笔者太菜,这里就跳过了。

查询子串

使用后缀数组可以 \(O(|S|log|T|)\) 查询主串 \(S\) 中是否存在子串 \(T\) ,由于后缀数组具有单调性,二分即可,比较两个字符串的字典序需要 \(O(|S|)\) ,总复杂度也就是 \(O(|S|log|T|)\)

height 数组

先引入概念 \(LCP\) 表示两个字符串的最长公共前缀。

height 数组就表示 \(lcp(sa[i],sa[i-1])\) ,也就是第 \(i\) 名和它前一名的后缀的最长公共前缀。

下文将 height 数组简称为 he。

先引入一个 Lemma ,\(he[rk[i]] \ge he[rk[i-1]] - 1\)

证明可以看:https://oi-wiki.org/string/sa

由于增量始终大于 \(-1\) ,所以可以线性处理出 \(he\) 数组。

查询两个子串的 LCP

\[lcp(sa[i],sa[j]) = min_{k=i+1}^j\{he[k]\} \]

这个式子可以感性理解,如果 \(he\) 一直大于某个数,前这么多位就一直没变过,反之,由于后缀已经排好序了,不可能变了之后变回来。

此时该问题转化为 RMQ 问题。

不同子串的数量

考虑所有子串都是某个后缀的前缀,将所有后缀排序。考虑对于相同的子串,在其出现的最大的后缀中记录贡献。于是对于每一个后缀,出现在后缀排序中下一个后缀的前缀不会被记录贡献,而没有出现的前缀一定会被记录贡献。

\[Ans = \frac{n(n+1)}{2} - \sum_{i=2}^n he[i] \]

比较一个字符串的两个子串的大小关系

如果比较的是 \(𝐴 =𝑆[𝑎...𝑏]\)\(𝐵 =𝑆[𝑐..𝑑]\) 的大小关系。

\(𝑙𝑐𝑝(𝑎,𝑐) \ge min(|𝐴|,|𝐵|)\)\(𝐴 < 𝐵 ⟺ |𝐴| < |𝐵|\)

否则,\(𝐴 <𝐵 ⟺ 𝑟𝑘[𝑎] <𝑟𝑘[𝑐]\)

posted @ 2026-01-07 05:08  Zheng_iii  阅读(7)  评论(0)    收藏  举报