• 后缀数组——倍增算法


    后缀数组,是指对给定序列(可以是字符串或者普通的数字数组,甚至可以是某些任意带权值的可比较数据),所有的后缀的排序,数据结构方面,主要包括两个数组:sa[i],rank[i],sa[i]记录当前排到第i的是谁,其实存储的就是其他的排序(如快排或者插入排序这一类依靠比较的排序算法)的最后结果,rank[i]则表示i这个字符开始的子串排第几,在这里,得到sa[i]利用了计数排序,关于计数排序,所以先介绍一下计数排序:

    计数排序所用到的数据结构:a[i]表示原始的序列,c[i]用来存储中间结果,sa[i]存储最后的结果,计数排序开始时需要用c[i]来存储数字i出现了几次,所以要求数据的范围不能过大,否则数组有可能开不下。

    算法过程:先得到一个数i在序列中出现的次数c[i],进而可以得到>=i的数有多少个,同样记录在c[i]中,这样就可以确定一个数应该出现在什么位置上了,代码如下:

     1 int a[1000],b[1000],c[1000];
     2 void counting_sort(int n)
     3 {
     4     memset(c,0,sizeof(c));
     5     for(i = 1;i <= n; i++)
     6         c[a[i]]++;//此时c[i]存储i出现的次数
     7     for(i = 2;i <= n; i++)
     8         c[i] += c[i - 1];//存储>=i的数字个数
     9     for(j = n;j > 0 ; j--)10         b[c[a[j]]--] = a[j];//得到排第b[c[a[j]]]的是a[j]
    11 }

    在后缀数组的倍增算法中,每次得到从i处起始2^k个字符的排序,2^k可以通过相连的两个2^(k-1)个字符的排序情况得到,我们可以设定超过字符串长度的部分值为0,在得到2^k长度子串的排序情况时,假设相连的两个2^(k-1)子串的排序情况分别为x,y则可以利用基数排序(注意这里不是计数排序,要搞清楚)的原理,把这2^k个字符的值定位xy,如x=10,y=20,则xy=1020,对aabaaaab的排序过程:

    代码如下:

    void sorting(int j)//基数排序
    {
        memset(sum,0,sizeof(sum));
        for (int i=1; i<=s.size(); i++) sum[ rank[i+j] ]++;
        for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1];
        for (int i=s.size(); i>0; i--) tsa[ sum[ rank[i+j] ]-- ]=i;//对第二关键字计数排序,tsa代替sa为排名为i的后缀是tsa[i] 
    
        memset(sum,0,sizeof(sum));
        for (int i=1; i<=s.size(); i++) sum[ rank[i] ]++;
        for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1];
        for (int i=s.size(); i>0; i--) sa[ sum[ rank[ tsa[i] ] ]-- ]= tsa[i]; //对第一关键字计数排序
        //构造互逆关系 
    }
    
    void get_sa()
    {
        int p;
        for (int i=0; i<s.size(); i++) trank[i+1]=s[i];
        for (int i=1; i<=s.size(); i++) sum[ trank[i] ]++;
        for (int i=1; i<=maxlen; i++) sum[i]+=sum[i-1];
        for (int i=s.size(); i>0; i--) 
            sa[ sum[ trank[i] ]-- ]=i;
        rank[ sa[1] ]=1;
        for (int i=2,p=1; i<=s.size(); i++)
        {
            if (trank[ sa[i] ]!=trank[ sa[i-1] ]) p++;
            rank[ sa[i] ]=p;
        }//第一次的sa与rank构造完成
        for (int j=1; j<=s.size(); j*=2)
        {
            sorting(j);
            trank[ sa[1] ]=1; p=1; //用trank代替rank 
            for (int i=2; i<=s.size(); i++)
            {
                if ((rank[ sa[i] ]!=rank[ sa[i-1] ]) || (rank[ sa[i]+j ]!=rank[ sa[i-1]+j ])) p++;
                trank[ sa[i] ]=p;//空间要开大一点,至少2倍
            }
            for (int i=1; i<=s.size(); i++) rank[i]=trank[i];
        }
    }
  • 相关阅读:
    《我是一只IT小小鸟》
    &&、||、?:、,四个运算符的求值顺序
    C Traps and Pitfalls 练习4.2
    “检测到LoaderLock”的解决办法
    VS中代码对齐等快捷键
    贪心 Greedy Algorithms
    这些最基本的程序优化方法你用过吗?
    内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局区[C++][内存管理][转载]
    [原创]让对话框的控件支持tooltips
    Debug 运行正常但 Release 失败的问题,Debug 和 Release 编译方式的本质区别
  • 原文地址:https://www.cnblogs.com/caozhenhai/p/2962243.html
Copyright © 2020-2023  润新知