后缀数组(Suffix Array)

前言

SA是一种解决多模板匹配问题的算法。大致就是将后缀处理出来然后按照字典序排个序。时间主要浪费在排序上。

sa数组sa[i]表示rk为i的后缀的开始位置。
rk数组rk[i]表示以i位置开始的后缀的rank为多少。

基数排序
先排个位,然后十位依次往下,稳定算法。

模板

const int N = 1e6 + 5; // 开二倍吧

int sa[N], rk[N], Height[N], tax[N], tp[N], a[N], n, m;
//rk[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] rk的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串

void Rsort() { //基数排序 要弄懂
    //rk第一关键字,tp第二关键字。
    for(int i = 0; i <= m; i++) tax[i] = 0;
    for(int i = 1; i <= n; i++) tax[rk[tp[i]]]++;
    for(int i = 1; i <= m; i++) tax[i] += tax[i - 1];
    for(int i = n; i >= 1; i--) sa[tax[rk[tp[i]]]--] = tp[i];//确保满足第一关键字的同时,再满足第二关键字的要求
}

int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; }
//通过二元组两个下标的比较,确定两个子串是否相同

void init() {
    memset(tp, 0, sizeof(tp));
    memset(rk, 0, sizeof(rk));
    memset(a, 0, sizeof(a));
}

void Suffix() {
    for(int i = 1; i <= n; i++) rk[i] = a[i], tp[i] = i;
    m = 128; Rsort();

    for(int w = 1, p = 1, i; p < n; w += w, m = p) {
        //w 当前一个子串的长度; m 当前离散后的排名种类数
        //当前的tp(第二关键字)可直接由上一次的SA的得到
        for(p = 0, i = n - w + 1; i <= n; i++) tp[++p] = i;
        for(int i = 1; i <= n; i++) if(sa[i] > w) tp[++p] = sa[i] - w;
        //更新SA值,并用tp暂时存下上一轮的rank(用于cmp比较)
        Rsort(); swap(rk, tp), rk[sa[1]] = p = 1;
        //用已经完成的SA来更新与它互逆的rank,并离散rank
        for(int i = 2; i <= n; i++) rk[sa[i]] = cmp(tp, sa[i], sa[i - 1], w) ? p : ++p;
    }
}

void cal_height() { //这个知道原理后就比较好理解程序
    int j, k = 0;
    for(int i = 1; i <= n; Height[rk[i++]] = k)
        for(k = k ? k-1 : k, j = sa[rk[i] - 1]; a[i + k] == a[j + k]; ++k);
}
int dp[N][20];
void ST() {
    for(int i = 1; i <= n; i++) dp[i][0] = Height[i];
    for(int j = 1; j <= 20; j++)
        for(int i = 1; i + (1 << j) - 1 <= n; i++)
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 <<(j - 1))][j - 1]);
}

int RMQ(int l, int r) {
    int k = log2(r - l + 1);
    return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值