acwing进阶课-后缀数组

介绍

char s[N];
int n, m, x[N], y[N], c[N], rk[N], height[N], sa[N];

void get_sa(){
    for(int i = 1; i <= n; i ++) c[x[i] = s[i]] ++;
    for(int i = 2; i <= m; i ++) c[i] += c[i - 1];
    for(int i = n; i; i--) sa[c[x[i]] --] = i;
    for(int k = 1; k <= n; k <<= 1){
        int num = 0;
        for(int i = n - k + 1; i <= n; i ++) y[++num] = i;
        for(int i = 1; i <= n; i ++){
            if(sa[i] > k) y[++num] = sa[i] - k;
        }
        for(int i = 1; i <= m; i ++) c[i] = 0;
        for(int i = 1; i <= n; i ++) c[x[i]] ++;
        for(int i = 2; i <= m; i ++) c[i] += c[i - 1];
        for(int i = n; i; i --) sa[c[x[y[i]]] --] = y[i], y[i] = 0;
        swap(x, y);
        x[sa[1]] = 1, num = 1;
        for(int i = 2; i <= n; i ++){
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ) ? num : ++ num ;
        }
        if(num == n) break;
        m = num;
    }
}

void get_height(){
    for(int i = 1; i <= n; i ++) rk[sa[i]] = i;
    for(int i = 1, k = 0; i <= n; i ++){
        if(rk[i] == 1) continue;
        if(k) k --;
        int j = sa[rk[i] - 1];
        while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++;
        height[rk[i]] = k;
    }
}

  1. 求一个字符串不同子串的数量
  2. 后缀排序之后,后缀长度-与排名靠前的一个最大公共前缀的和就是答案
  3. 题目中倒着遍历,然后维护排名前后的一个相对关系就可以了
#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 2e5+10;
unordered_map<int, int> h;
int cnt;
LL ans[N];
int L[N], R[N];

// 数组长度开两倍
int s[N];
int n, x[N], y[N], c[N], rk[N], height[N], sa[N];

void get_sa(){
    int m = 1e5;
    for(int i = 1; i <= n; i ++) c[x[i] = s[i]] ++;
    for(int i = 2; i <= m; i ++) c[i] += c[i - 1];
    for(int i = n; i; i--) sa[c[x[i]] --] = i;
    for(int k = 1; k <= n; k <<= 1){
        int num = 0;
        for(int i = n - k + 1; i <= n; i ++) y[++num] = i;
        for(int i = 1; i <= n; i ++){
            if(sa[i] > k) y[++num] = sa[i] - k;
        }
        for(int i = 1; i <= m; i ++) c[i] = 0;
        for(int i = 1; i <= n; i ++) c[x[i]] ++;
        for(int i = 2; i <= m; i ++) c[i] += c[i - 1];
        for(int i = n; i; i --) sa[c[x[y[i]]] --] = y[i], y[i] = 0;
        swap(x, y);
        x[sa[1]] = 1, num = 1;
        for(int i = 2; i <= n; i ++){
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ) ? num : ++ num ;
        }
        if(num == n) break;
        m = num;
    }
}

void get_height(){
    for(int i = 1; i <= n; i ++) rk[sa[i]] = i;
    for(int i = 1, k = 0; i <= n; i ++){
        if(rk[i] == 1) continue;
        if(k) k --;
        int j = sa[rk[i] - 1];
        while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++;
        height[rk[i]] = k;
    }
}

int get(int x){
    if(h.count(x) == 0) h[x] = ++ cnt;
    return h[x];
}

int main(){
    scanf("%d", &n);
    for(int i = n; i >= 1; i --) scanf("%d", &s[i]), s[i] = get(s[i]);
    get_sa();
    get_height();
    LL res = 0;
    R[0] = 1;
    L[n + 1] = n;
    for(int i = 1; i <= n; i ++){
        res += n - sa[i] + 1 - height[i];
        L[i] = i - 1;
        R[i] = i + 1;
    }
    for(int i = 1; i <= n; i ++){
        ans[i] = res;
        int now = rk[i], nxt = R[now], after = L[now];
        res -= n - sa[now] + 1 - height[now];
        res -= n - sa[nxt] + 1- height[nxt];
        height[nxt] = min(height[nxt], height[now]);
        res += n - sa[nxt] + 1 - height[nxt];
        L[nxt] = after; R[after] = nxt;
    }
    for(int i = n; i >= 1; i --){
        printf("%lld\n", ans[i]);
    }
    // puts("");
    return 0;
}

posted @ 2022-04-04 14:45  牛佳文  阅读(72)  评论(0编辑  收藏  举报