您的位置:首页 > 其它

后缀数组模板

2016-03-27 00:01 281 查看

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

 

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

  //counting sort
for(int i=0; i<m; i++) cnt[i]=0;
for(int i=0; i<n; i++) cnt[rk[i]=s[i]]++;
for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for(int i=n-1; i>=0; i--) sa[--cnt[rk[i]]]=i;  //stable sort

for(int len=1; len<n; len*=2){
//stp.1 fill sa2[]
int p=0;
for(int i=n-len; i<n; i++) sa2[p++]=i;
for(int i=0; i<n; i++) if(sa[i]>=len) sa2[p++]=sa[i]-len;
//stp.2 fill sa[], countig sort
for(int i=0; i<m; i++) cnt[i]=0;
for(int i=0; i<n; i++) cnt[rk[i]]++;
for(int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for(int i=n-1; i>=0; i--)
sa[--cnt[rk[sa2[i]]]]=sa2[i];
//stp.1 and stp.2 together is radix sort

//fill rk[]
swap(rk, sa2);
rk[sa[0]]=0;
for(int i=1; i<n; i++)
rk[sa[i]]=rk[sa[i-1]]+!same(sa2, sa[i-1], sa[i], len);

m=rk[sa[n-1]]+1;
if(m==n) break;
}

//CALCULATE hgt[]

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;
}
}

 

 

注意:

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
=0
rk
=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
    ]了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: