• 初识后缀数组


    推荐博客:https://www.cnblogs.com/zinthos/p/3899725.html

    所谓的后缀数组,就是将字符串的 n 个后缀全部取出来,采用字典序的排序方式,将排序好的后缀开头位置顺次放进数组中。

    在后缀数组中,有几个关键的变量

    1 . SA 数组,若 sa[ i ] = j ,所表示的含义是排名为 i 的后缀起始下标是 j ,

    2 . rank数组,若rank[ i ] = j,所表示的含义是以 i 为开头的后缀的排名是 j,

    3 . height数组,所表示的suffix(sa[i])和suffix(sa[i-1])的最长公共前缀,即排名相邻的两个后缀的最长公共前缀

    4 .h数组, 所表示的含义是 h[i] = height[rank[i]],即 suffix(i)和在有序数列中排在它前一名后缀的最长公共前缀。

     suffix(j) 和 suffix(k)的最长公共前缀为 min(height[rank[j]+1], height[rank[j]+2], … height[rank[k]])

    接下来就是构造后缀数组,有两种方法,一种是倍增的算法(n*logn)

    const int maxn = 2e4+5;
    
    /*
    -待排序数组长度为 n,放在 0~n-1,在最后加一个'$',让其比所有的字符串中的字符都小,
    这样既可以满足字典序的要求,也不越界
    *build_sa( ,n+1, );//注意是n+1;
    *getheight(,n);
    *例如:
    *n   = 8;
    *num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
    *rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
    *sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
    *height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
    */
    
    int n; 
    int a[maxn], s[maxn];
    int sa[maxn], rank[maxn], height[maxn];
    int t1[maxn], t2[maxn], c[maxn];
    
    bool cmp(int *r, int a, int b, int l){
        return r[a] == r[b] && r[a+l] == r[b+l]; // !!!
    }
    
    void build_sa(int n, int m){ //m代表估计数字,是ASCll的最大值
        int *x = t1, *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 j = 1; j <= n; j <<= 1){  // !!!
            int p = 0;
            for(int i = n-j; i < n; i++) y[p++] = i;
            for(int i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i]-j;
            
            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]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++;
           
            if (p >= n) break;
            m = p; // 下次基数排序的最大值  !!!
        }
    }
    
    void getheight(){
        int k = 0;
        for(int i = 0; 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;
        }
    }
    
    东北日出西边雨 道是无情却有情
  • 相关阅读:
    memcached
    Springboot配置类
    程序员中的长期主义者-webpack
    程序员中的长期主义者-css
    程序员中的长期主义者
    h5字体样式太小引入UI库字体也很小看不清
    NodeJs正则关键字查找目录文件
    普通二维码跳转微信小程序
    git版本回滚
    绕不过的就突破
  • 原文地址:https://www.cnblogs.com/ccut-ry/p/9048665.html
Copyright © 2020-2023  润新知