不理解后缀数组的童鞋,可以看看挑战程序设计书上讲的,非常好,不过那上面讲的使是用sort进行的排序,效率较基数排序低。不过思想一样,那上面看懂了看这个很快。
说三点:
(1)y[]数组表示按照第二关键字排号的情况,目前里面存的时第一关键字的值,比如 y[1]=4,表示第二关键字排在第1位上此时第一关键字为下标从4开始的子字符串。因此用基数排序,因为其相当稳定。
(2)swap交换后,计算x此时x就代表rank数组,相当于rank[sa[i]]=i,因为存在相等的子串,所以要判断一下。
(3)排好序的时候,因为没有后缀再相同,所以此时p>=n
附加一份我理解过程中输出中间结果的代码:https://paste.ubuntu.com/p/fcDYPzpCbD/,自己动手理解的更快。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000;
const int maxasc = 128; //ascII码[0,127]
char s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn],n;
void debug()
{
for(int i=0; i<n; i++) printf("%d ",sa[i]);
printf("\n");
}
void build_sa(int m)
{
int *x = t1, *y = t2;
for(int i=0; i<m; i++) c[i] = 0;
for(int i=0; i<n; i++) c[x[i] = s[i]]++;
for(int i=1; i<m; i++) c[i] += c[i-1];
for(int i=n-1; i>=0; i--) sa[--c[x[i]]] = i;
cout<<"x[ ";
for(int i=0;i<n;i++){
cout<<x[i]<<" ";
}
cout<<" ]"<<endl;
cout<<"sa[ ";
for(int i=0;i<n;i++){
cout<<sa[i]<<" ";
}
cout<<" ]"<<endl;
for(int k=1; k<=n; k = k<<1)
{
int p = 0;
for(int i=n-k; i<n; i++) y[p++] = i;
for(int i=0; i<n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
cout<<"y[ ";
for(int i=0;i<n;i++){
cout<<y[i]<<" ";
}
cout<<" ]"<<endl;
for(int i=0; i<m; i++) c[i] = 0;
for(int i=0; i<n; i++) c[x[y[i]]]++;
for(int i=0; i<m; i++) c[i] += c[i-1];
for(int i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
cout<<"sa[ ";
for(int i=0;i<n;i++){
cout<<sa[i]<<" ";
}
cout<<" ]"<<endl;
swap(x,y);
p = 1; x[sa[0]] = 0;
for(int i=1; i<n; i++)
x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k] ? p-1 : p++;
cout<<"x[ ";
for(int i=0;i<n;i++){
cout<<x[i]<<" ";
}
cout<<" ]"<<endl;
if(p >= n) break;
m = p;
}
}
int main()
{
scanf("%s",s);
n = strlen(s);
build_sa(maxasc);
return 0;
}
计蒜客的另外一份模板,没有下面这个好理解,但先放着把,毕竟最好用。
#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=210000;
char s[MAX_N];
int sa[MAX_N],t[MAX_N],t2[MAX_N],c[MAX_N],n;
void build_sa(int m){
int i,*x=t,*y=t2;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1){
int p=0;
for(i=n-k;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n) break;
m=p;
}
}
int Rank[MAX_N],h[MAX_N];
void get_h(){
int i,j,k=0;
for(i=0;i<n;i++) Rank[sa[i]]=i;
for(i=0;i<n;i++){
if(k) k--;
if(Rank[i]){
j=sa[Rank[i]-1];
while(s[i+k]==s[j+k]) k++;
h[Rank[i]]=k;
}
}
}
int main() {
cin>>s;
n=strlen(s);
build_sa(131);
get_h();
return 0;
}