SA


后缀数组 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;
    }
}
View Code

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$

原文地址:https://www.cnblogs.com/Urushibara-Ruka/p/13124452.html