后缀数组 Suffix Array
$M$ : 当前需要排序的集合大小
$rank[i]$ : 当前以第 $i$ 位开头的后缀的排名,第一关键字
$sa[i]$ : 第 $i$ 名的后缀的开头位置
$tmp[i]$ : 第二关键字的排名为 $i$ 的后缀开头位置
$tax[i]$ : 计数桶,集合内某元素出现次数

void rsort() { for (int i = 1; i <= M; i++) tax[i] = 0; for (int i = 1; i <= N; i++) tax[rak[i]]++; for (int i = 2; i <= M; i++) tax[i] += tax[i - 1]; for (int i = N; i >= 1; i--) sa[tax[rak[tmp[i]]]--] = tmp[i]; //按第二关键字从大到小枚举后缀再标记,当第一关键字相同时,第二关键字大的后缀排在后面 } void init() { for (int i = 1; i <= N; i++) rak[i] = s[i] - '0' + 1, tmp[i] = i; M = 75; rsort(); for (int len = 1, p = 0; p < N; M = p, len <<= 1) { // p == N 时退出, 因为只要所有后缀排名互不相同时就已排序完毕,再排下去不会改变结果,可提前退出 num = 0; for (int i = N - len + 1; i <= N; i++) tmp[++num] = i; for (int i = 1; i <= N; i++) if (sa[i] > len) tmp[++num] = sa[i] - len; rsort(); swap(tmp, rak); rak[sa[1]] = p = 1; for (int i = 2; i <= N; i++) rak[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + len] == tmp[sa[i - 1] + len] ? p : ++p); } int l = 0; for (int i = 1; i <= N; i++) { if (l) l--; int j = sa[rak[i] - 1]; while (s[i + l] == s[j + l]) l++; height[rak[i]] = l; } }
height数组性质证明
$height[i]$ : 排名为 $i$ 的后缀与 $i - 1$ 的的 $lcp$(最长公共前缀)
性质
$$height[rak[i]] geq height[rak[i - 1]] - 1$$
证明
当$height[i - 1] geq 1$时
设 $rak[k] = rak[i - 1] - 1$, 则有 $rak[k + 1] leq rak[i] - 1$
$lcp(suffix[rak[i]], suffix[rak[i] - 1]) geq lcp(suffix[rak[i]], suffix[rak[k + 1]])$
$lcp(suffix[rak[i]], suffix[rak[i] - 1]) geq lcp(suffix[rak[i - 1]], suffix[rak[k]]) - 1$
$lcp(suffix[rak[i]], suffix[rak[i] - 1]) geq lcp(suffix[rak[i - 1]], suffix[rak[i - 1] - 1]) - 1$