后缀数组求不同子串的个数

洛谷P2408

如果学会后缀数组,那么这题就是一个对于后缀数组的结果的应用,具体看solve函数的注释

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;

struct SuffixArray {

    ///记住数组从1开始
    /// SA后缀数组, rak排名, height

    static const int MAXN = 1e6 + 10;

    int N, M, rak[MAXN], sa[MAXN], tax[MAXN], tp[MAXN], Height[MAXN];

    char s[MAXN];

    void Qsort() {
        for(int i = 0; i <= M; i++) tax[i] = 0;
        for(int i = 1; i <= N; i++) tax[rak[i]]++;
        for(int i = 1; i <= M; i++) tax[i] += tax[i - 1];
        for(int i = N; i >= 1; i--) sa[ tax[rak[tp[i]]]-- ] = tp[i];
    }

    void SuffixSort() {
        M = 300;
        if(N == 0) N = strlen(s + 1);
        for(int i = 1; i <= N; i++) rak[i] = s[i], tp[i] = i;
        Qsort();
        for(int w = 1, p = 0; p < N; M = p, w <<= 1) {
            p = 0;
            for(int i = 1; i <= w; i++) tp[++p] = N - w + i;
            for(int i = 1; i <= N; i++) if(sa[i] > w) tp[++p] = sa[i] - w;
            Qsort();
            std::swap(tp, rak);
            rak[sa[1]] = p = 1;
            for(int i = 2; i <= N; i++)
                rak[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
        }
    }

    void GetHeight() {
        int j, k = 0;
        for(int i = 1; i <= N; i++) {
            if(k) k--;
            int j = sa[rak[i] - 1];
            while(s[i + k] == s[j + k]) k++;
            Height[rak[i]] = k;
        }
    }

    void debug() {
        printf("id    :");
        for (int i = 1; i <= N; i++ ) printf("%-3d ", i); puts("");
        printf("rank  :");
        for (int i = 1; i <= N; i++ ) printf("%-3d ", rak[i]); puts("");
        printf("sa    :");
        for (int i = 1; i <= N; i++ ) printf("%-3d ", sa[i]); puts("");
        printf("height:");
        for (int i = 1; i <= N; i++ ) printf("%-3d ", Height[i]); puts("");
    }

    void solve() {
        /**
        给你一个长为N的字符串,求不同的子串的个数?
        对于一个后缀sa[i],它产生了n-sa[i]个前缀,减去height[i]个相同的前缀(与前一个比较),
        则产生了n-sa[i]-height[i]个子串。累加后即结果。
        */
        long long ans = 0;
        for(int i = 1; i <= N; i++ ) {
            ans += N + 1 - sa[i] - Height[i];
        }
        cout << ans << endl;
    }

} cwl;

int main() {
    while(~scanf("%d", &cwl.N) && cwl.N) {
        scanf("%s", cwl.s + 1);
        cwl.SuffixSort();
        cwl.GetHeight();
//        cwl.debug();
        cwl.solve();
    }

    return 0;
}