• 后缀数组模板


    罗穗骞论文《后缀数组--处理字符串的有力工具》中的实现修改后的版本。

     1 void SA(int *s, int *sa, int *sa2, int *rk, int *cnt, int *hgt, int n, int m){
     2 
     3   //counting sort
     4     for(int i=0; i<m; i++) cnt[i]=0;
     5     for(int i=0; i<n; i++) cnt[rk[i]=s[i]]++;
     6     for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
     7     for(int i=n-1; i>=0; i--) sa[--cnt[rk[i]]]=i;  //stable sort
     8   
     9     for(int len=1; len<n; len*=2){
    10         //stp.1 fill sa2[]
    11         int p=0;
    12         for(int i=n-len; i<n; i++) sa2[p++]=i;
    13         for(int i=0; i<n; i++) if(sa[i]>=len) sa2[p++]=sa[i]-len;
    14         //stp.2 fill sa[], countig sort
    15         for(int i=0; i<m; i++) cnt[i]=0;
    16         for(int i=0; i<n; i++) cnt[rk[i]]++;
    17         for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
    18         for(int i=n-1; i>=0; i--) 
    19             sa[--cnt[rk[sa2[i]]]]=sa2[i];
    20         //stp.1 and stp.2 together is radix sort
    21 
    22         //fill rk[]
    23         swap(rk, sa2);
    24         rk[sa[0]]=0;
    25         for(int i=1; i<n; i++)
    26             rk[sa[i]]=rk[sa[i-1]]+!same(sa2, sa[i-1], sa[i], len);
    27 
    28         m=rk[sa[n-1]]+1;
    29         if(m==n) break;
    30     }
    31 
    32     //CALCULATE hgt[]
    33 
    34     for(int i=0, j, lcp=0; i<n-1; i++){
    35         lcp?--lcp:0;
    36         // rk[i]>0
    37         j=sa[rk[i]-1];
    38         for(; s[j+lcp]==s[i+lcp]; lcp++);
    39         hgt[rk[i]]=lcp;
    40     }  
    41 }

    注意:

    1.s[ ]数组的末尾必须补一个“0”,这里“0”的含义是:比s[ ]中其他元素都小的一个值。因而函数

    void SA(int *s, int *sa, int *sa2, int *rk, int *cnt, int *hgt, int n, int m)

    的参数 n 是 s[ ] 的实际长度再加 1 .

    2.由于s[ ]的末尾补了零,必然有:

      sa[0]=n (0-indexed)

      rk[n]=0
    rk[n]=0,意味着rk[0...n-1] > 0

    所以求hight数组时,循环才可写成:

    for(int i=0, j, lcp=0; i<n-1; i++){
        lcp?--lcp:0;
        // rk[i]>0
        j=sa[rk[i]-1];
        for(; s[j+lcp]==s[i+lcp]; lcp++);
        hgt[rk[i]]=lcp;
    }  

    因为s[rk[i]-1]不会越界。补"0"以后,hgt[1...n]都变得 well-defined 了。

    3. 在函数SA()中,参数rk和sa作为两个指针使用,由于有

    swap(rk, sa2);

    这一句,在 SA() 外部,我们不知道 sa2[] 和 rk[] 这两个数组里装的分别是什么,这时如果要用 rk[] 数组必须从sa[] 数组重新构造一遍:

    for(int i=0; i<=n; i++) rk[sa[i]]=i;

    注意:

    1. 这里的 n 指的是数组的实际长度, 而非末尾补 "0" 后的长度 (后者是作为SA()的参数的 n), 下同
    2. 因此, for-head 里的判断 i<=n 不能写成 i<n ,因为 sa[ ] 数组是按照补 “0” 后的 s[ ] 数组算得的,必然有 sa[0]=n , 若写成 < 就漏掉 rk[sa[n]]了.
  • 相关阅读:
    SVN中trunk、branches、tag的使用
    svn建立分支和svn代码合并的操作方法
    SVN分支的合并和同步
    iOS开发--即时通讯
    iOS 开发--开源图片处理圆角
    ios开发--网易滚动导航栏
    ios开发--高德地图SDK使用简介
    大型网站架构演变和知识体系
    Nginx配置文件nginx.conf中文详解
    nginx+apache+php+mysql服务器集群搭建
  • 原文地址:https://www.cnblogs.com/Patt/p/5324592.html
Copyright © 2020-2023  润新知