zoukankan      html  css  js  c++  java
  • 学习笔记:后缀数组

    后缀数组是指对于后缀排序后,每个后缀的位置:sa[rank]=pos:排名为rank的后缀是pos->len这个后缀

    note:rank[pos]=rank:位置为pos的串排名为rank

    白书上的代码简洁明了,很容易理解。

    核心思想:我们对于每个位置开始的后缀,不直接计算,先计算从这个位置开始,向后1位是第几小,然后向后2位,向后4位,一直到*2>n,这时就算好了后缀数组

    复杂度:O(n*log(n)^2) :倍增log(n),快排log(n)

    2017/1/8 lcp中相邻的两个字符串是这两个字符串的最大lcp,和左和右谁大不确定(好像听不懂)

    似乎听了课之后又有了些感悟:为什么pos+1的lcp至少是pos的lcp-1,因为pos+1位置前面至少会有pos前一个后缀截掉第一个字符,肯定比pos+1小,并且两个东西lcp为pos的lcp-1

    代码:

    bool cp(int x,int y)
    {
        //rank(2*k)[x]和rank(2*k)[y]可以由rank(k)[x],rank(k)[x+k]和rank(k)[y],rank(k)[y+k]比较得出
        if(rank[x]!=rank[y]) return rank[x]<rank[y];
        int rx=x+k<=n?rank[x+k]:-1;//当前位置是x,向后k位超出了长度,那么很明显,rank[x+k]是不存在的,也可以看做一个空串,rank=-1
        int ry=y+k<=n?rank[y+k]:-1;
        return rx<ry;
    }
    void sa(string s)
    {
        //******排名:第几小
        n=s.length();
        for(int i=0;i<=n;i++)
        {
            sa[i]=i; rank[i]=i<n?s[i]:-1;
        }
        for(k=1;k<=n;k*=2)
        {
            sort(sa,sa+n+1,cp);//sa排序,sa存的是每个排名的位置,也就是每个排名对应的串
            //sa[0]即为排名为0的串对应的位置
            temp[sa[0]]=0;//第一个rank为0
            for(int i=1;i<=n;i++)
            {
                temp[sa[i]]=temp[sa[i-1]]+(cp(sa[i-1],sa[i]));//计算名次,因为sa已经排好序,这里计算如果两个串相等,那么排名一样,否则排名+1
            }
            for(int i=0;i<=n;i++)
            {
                rank[i]=temp[i];//直接赋值,上面那个因为是按sa排序,所以按sa赋值
            }
        }
    }

     基数排序

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    int n; 
    char c[N];
    int s[N], sa[N], lcp[N], a[N], b[N], rank[N];
    void radix(int *s, int *a, int *b, int n, int m)
    {
        int count[N]; memset(count, 0, sizeof(count));
        for(int i = 1; i <= n; ++i) ++count[s[a[i]]];
        for(int i = 1; i <= m; ++i) count[i] += count[i - 1];
        for(int i = n; i; --i) b[count[s[a[i]]]--] = a[i];
    }
    void Sa()
    {
        for(int i = 1; i <= n; ++i) rank[i] = i;
        radix(s, rank, sa, n, 26);
        rank[sa[1]] = 1;
        for(int i = 2; i <= n; ++i) rank[sa[i]] = rank[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
        for(int k = 1; k <= n; k <<= 1)
        {
            for(int i = 1; i <= n; ++i) 
            {
                a[i] = rank[i];
                b[i] = i + k <= n ? rank[i + k] : 0;
                sa[i] = i;
            }    
            radix(b, sa, rank, n, n + 1);
            radix(a, rank, sa, n, n + 1);
            rank[sa[1]] = 1;
            for(int i = 2; i <= n; ++i) rank[sa[i]] = rank[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]] || b[sa[i]] != b[sa[i - 1]]);
        }
    }
    void Lcp()
    {
        int h = 0; 
        for(int i = 1; i <= n; ++i) rank[sa[i]] = i;
        for(int i = 1; i <= n; ++i)
        {
            int j = sa[rank[i] - 1];
            if(rank[i] <= 1) continue;
            if(h > 0) --h;
            for(; i + h <= n && j + h <= n; ++h) if(s[i + h] != s[j + h]) break;
            lcp[rank[i] - 1] = h;    
        }
    }
    int main()
    {
        scanf("%s", c + 1); n = strlen(c + 1);
        for(int i = 1; i <= n; ++i) s[i] = c[i] - 'a' + 1;
        Sa(); Lcp();
        for(int i = 1; i <= n; ++i) printf("%d ", sa[i]);
        puts("");
        for(int i = 1; i < n; ++i) printf("%d ", lcp[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    canvas设置渐变
    canvas设置线条样式
    canvas给图形添加颜色
    Vue中父组件与子组件之间传值
    Vue实例的生命周期
    es6常用语法和特性
    JS基础:常用API
    JS基础:函数
    JS基础:闭包和作用域链
    JS基础:this的指向以及apply、call的作用
  • 原文地址:https://www.cnblogs.com/19992147orz/p/6250697.html
Copyright © 2011-2022 走看看