SPOJ 694 后缀数组

简略题意:求不相同的子串的个数。

求出sa之后,会发现每按名次加入一个后缀,会产生n-sa[i]个新的子串,其中有height[i]个之前已经出现过。答案为n-sa[i]+height[i]。

#include <iostream>
#include <cstring>
#include <map>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

struct SuffixArray{
    static const int maxn = 1e6+7;
    int s[maxn], n, m;
    int sa[maxn], rank[maxn], height[maxn];
    int t[maxn], t2[maxn], c[maxn];
    int MIN[maxn][30];
    void build_sa(){
        s[n++] = 0;
        int *x = t, *y = t2;
        for(int i=0; i<m; i++) c[i] = 0;
        for(int i=0; i<n; i++) c[x[i]=s[i]]++;
        for(int i=1; i<m; i++) c[i]+=c[i-1];
        for(int i=n-1; i>=0; i--) sa[--c[x[i]]] = i;
        for(int k=1; k<=n; k<<=1){
            int p = 0;
            for(int i=n-k; i<n; i++) y[p++] = i;
            for(int i=0; i<n; i++) if(sa[i]>=k) y[p++] = sa[i]-k;
            for(int i=0; i<m; i++) c[i] = 0;
            for(int i=0; i<n; i++) c[x[y[i]]]++;
            for(int i=1; i<m; i++) c[i]+=c[i-1];
            for(int i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
            swap(x, y);
            p = 1; x[sa[0]] = 0;
            for(int i=1; i<n; i++)
                x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
            if(p >= n) break;
            m=p;
        }
        n--;
    }
    void getheight(){
        int k = 0;
        for(int i=1; i<=n; i++) rank[sa[i]] = i;
        for(int i=0; i<n; i++){
            if(k) k --;
            int j = sa[rank[i]-1];
            while(s[i+k] == s[j+k]) k++;
            height[rank[i]] = k;
        }
    }
    void RMQ_init(){
        for(int i=0; i<n; i++) MIN[i][0] = height[i+1];
        for(int j=1; (1<<j)<=n; j++){
            for(int i=0; i+(1<<j)<=n; i++){
                MIN[i][j] = min(MIN[i][j-1], MIN[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int RMQ(int L, int R){
        int k = 0;
        while((1<<(k+1)) <= R-L+1) k++;
        return min(MIN[L][k], MIN[R-(1<<k)+1][k]);
    }
    int LCP(int i, int j){
         if(rank[i] > rank[j]) swap(i, j);
        return RMQ(rank[i], rank[j]-1);
    }
    void init(char *str, int _n){
        m = 130;
        n = _n;
        memset(height, 0, sizeof height);
        for(int i=0; i<n; i++)
            s[i] = (int)str[i];
        build_sa();
        getheight();
        RMQ_init();
    }
    int check(int x, int k) {
        int cnt = 1;
        for(int i = 2; i <= n; i++) {
            if(height[i] >= x) {
                cnt ++;
                if(cnt >= k) return 1;
            } else {
                cnt = 1;
            }
        }
        return cnt >= k;
    }
    void solve() {
        int cnt = 0;
        for(int i = 1; i <= n; i++)
            cnt += n - sa[i] - height[i];
        printf("%d\n", cnt);
    }
}suffix;

int t;
char str[1100];

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%s", str);
        suffix.init(str, strlen(str));
        suffix.solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值