后缀数组学习笔记
做题做到一半突然发现自己不会后缀数组(?)
后缀数组主要由两个数组组成,sa 和 rk。
sa[i] 表示开头位置为 i 的后缀的字典序排名。
rk[i] 表示字典序排名为 i 的后缀的开头位置。
可以采用倍增法求解:
考虑对于 \(w=2^k\),求出仅考虑长度为 \(w\) 的前缀时的 sa 以及 rk,那么利用 \(i\) 和 \(i+w\) 的信息就可以推导出 \(k+1\) 时的 sa 和 rk。
我们考虑求出 sa,然后推出 rk。
最简单的方法是利用双关键字排序,令 sa[i] 为第一关键字,sa[i+w] 为第二关键字,排序即可得到 \(k+1\) 时的 sa。
进阶的方法是考虑 sa[i+w] 对 sa[i] 的影响,我们找到数组 id[i] 表示第二关键字排名为 \(i\) 的后缀的开头位置。
那么只需要按类似的方法扫一遍即可。rk 的递推方法类似,具体可以看代码。
#define rep(i,a,b) for(int i(a);i<(b);++i)
#define dep(i,a,b) for(int i(a-1);i>=(b);--i)
std::pair<std::vector<int>, std::vector<int>> SuffixArray(const std::string& str) {
int n = str.length(), m = 0xff;
std::vector<int> rk(n, 0), sa(n, 0), _rk(n, 0), id(n, 0), cnt(std::max(n, m), 0);
rep(i, 0, n) ++cnt[rk[i] = str[i]];
rep(i, 1, m) cnt[i] += cnt[i-1];
dep(i, n, 0) sa[--cnt[rk[i]]] = i;
for(int w = 1;; w <<= 1) {
int j = 0, p = 0;
rep(i, n-w, n) id[j++] = i;
rep(i, 0, n) if(sa[i] >= w) id[j++] = sa[i] - w;
std::fill(cnt.begin(), cnt.begin() + m, 0);
rep(i, 0, n) ++cnt[rk[i]];
rep(i, 1, m) cnt[i] += cnt[i-1];
dep(i, n, 0) sa[--cnt[rk[id[i]]]] = id[i];
_rk.swap(rk);
auto at = [&](int x) ->int {return x >= n ? -1 : _rk[x];};
rk[sa[0]] = p = 0;
rep(i, 1, n)
rk[sa[i]] = (at(sa[i]) != at(sa[i-1]) || at(sa[i] + w) != at(sa[i-1] + w)
? ++p : p);
if((m = ++p) == n) break;
}
return {sa, rk};
}
就酱。
height 数组,下文简称为 h,表示 LCP(sa[i],sa[i-1])
用来求任意 LCP 的。
显然两个后缀的 LCP 就等于他们的 rk 之间的 h 的最小值。
有一个性质是 \(\text{h[rk[i]]}\ge\text{h[rk[i-1]]-1}\)。
于是暴力枚举即可。
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/18986645

浙公网安备 33010602011771号