CP Notebook

← all categories

Suffix Array 1a145bb6

Suffix array via radix-sort doubling, with the matching O(n) LCP pass woven into the same construction (kactl's SuffixArray). sa[i] is the starting index of the i'th suffix in sorted order; sa.size() == s.size()+1 and sa[0] == s.size() (the appended sentinel, sorts first). lcp[i] = longest common prefix of sa[i] and sa[i-1] (lcp[0] = 0). s must not contain a NUL character. Source: kactl (CC0), see NOTICE.md.

Time: O(n \log n). kactl-derived, tested

content/strings/suffix-array.h

struct SuffixArray {
    vector<int> sa, lcp;

    SuffixArray(string s, int lim = 256) {
        s.push_back(0);
        int n = (int)s.size(), k = 0, a, b;
        vector<int> x(s.begin(), s.end()), y(n), ws(max(n, lim));
        sa = lcp = y;
        iota(sa.begin(), sa.end(), 0);
        for (int j = 0, p = 0; p < n; j = max((ll)1, j * 2), lim = p) {
            p = j;
            iota(y.begin(), y.end(), n - j);
            rep(i, 0, n) if (sa[i] >= j) y[p++] = sa[i] - j;
            fill(ws.begin(), ws.end(), 0);
            rep(i, 0, n) ws[x[i]]++;
            rep(i, 1, lim) ws[i] += ws[i - 1];
            for (int i = n; i--;) sa[--ws[x[y[i]]]] = y[i];
            swap(x, y);
            p = 1;
            x[sa[0]] = 0;
            rep(i, 1, n) {
                a = sa[i - 1], b = sa[i];
                x[b] = (y[a] == y[b] && y[a + j] == y[b + j]) ? p - 1 : p++;
            }
        }
        for (int i = 0, j; i < n - 1; lcp[x[i++]] = k)
            for (k && k--, j = sa[x[i] - 1]; s[i + k] == s[j + k]; k++);
    }
};