后缀数组模板

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

 

 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]]了.
posted @ 2016-03-27 00:01  Pat  阅读(254)  评论(0编辑  收藏  举报