后缀数组(suffix array)模板
后缀数组排序的思想很容易理解,代码也被大牛没极度精简了~~有时间可以好好看一下其中各种优化的细节。
而后缀数组的应用多与其中的height数组相关,所以对height数组性质的把握和理解是至关重要的,网上大部分代码没有很好的注释,这里给出一个注释版的。
#include <cstdio> #include <string> #include <vector> #include <cstring> #include <utility> #include <algorithm> using namespace std; const int MAXN = 100005; int sa[MAXN], rank[MAXN], height[MAXN]; char str[MAXN]; int wa[MAXN], wb[MAXN], wv[MAXN], ws[MAXN]; inline bool cmp(int *r, int a, int b, int len){ return (r[a]==r[b]) && (r[a+len]==r[b+len]); } void sortSA(char *r, int *sa, int n, int m){//r为字符串数组,sa为后缀数组,n=strlen(s)+1,m为max(r[i])+1。 int i, j, p, *x = wa, *y = wb, *t; //对长度为1的字符串基数排序。 for(i = 0; i < m; i++) ws[i] = 0;//清零。 for(i = 0; i < n; i++) ws[x[i]=r[i]]++;//统计各相同字符的个数。 for(i = 1; i < m; i++) ws[i] += ws[i-1];//统计小于等于i的字符共有多少个。 for(i = n-1; i >= 0; i--) sa[--ws[x[i]]] = i;//小于等于r[i]共有ws[x[i]]个,因此r[i]排在第ws[x[i]]个。 for(j = p = 1; p < n; j <<= 1, m = p){//p是第二关键字为0的个数,j是当前比较的字符串长度. //对第二关键字基数排序。 //y[s]=t表示排在第s个的起点在t,即y[s]对第二关键字排序,但y[s]的值指向第一关键字的位置。 for(p=0, i=n-j; i < n; i++) y[p++] = i;//在n-j之后的第二关键字都为0,排在前面,即第p个。 for(i = 0; i < n; i++){ if(sa[i] >= j)//如果排在第i个的字符串起点在sa[i],满足sa[i]>=当前字符串长度j。 y[p++] = sa[i] - j;//对于sa[i]-j为起点的第二关键字排在前面。 } //对第一关键字基数排序。 for(i = 0; i < m; i++) ws[i] = 0;//清零。 for(i = 0; i < n; i++) ws[wv[i]=x[y[i]]]++;//第二关键字排在第i个的起点在y[i],x[y[i]]就是y[i]指向的字符,ws进行个数统计。 for(i = 1; i < m; i++) ws[i] += ws[i-1];//统计字符小于等于i的个数。 for(i = n-1; i >= 0; i--)//wv[i]是排在第i个第二关键字对应的第一关键字。 sa[--ws[wv[i]]] = y[i];//y[i]就是第一关键字的位置。 for(t=x,x=y,y=t,x[sa[0]]=0,p=i=1; i < n; i++)//交换x,y的地址,x保存当前rank值,y为前一次rank值。 x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p-1:p++; //若rank[sa[i-1]]=rank[sa[i]],则必然sa[i-1]+j没有越界,因为不可能有相等的后缀。 } } /* height 数组:定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公 共前缀,也就是排名相邻的两个后缀的最长公共前缀。那么对于j 和k,不妨设 rank[j]<rank[k],则有以下性质: suffix(j) 和suffix(k) 的最长公共前缀为height[rank[j]+1], height[rank[j]+2], height[rank[j]+3], … ,height[rank[k]]中的最小值。 */ void calHeight(char *r, int *sa, int n){ //n为字符数组r的长度 int i, j, k = 0; for(i = 1; i <= n; i++) rank[sa[i]] = i; for(i = 0; i < n; i++){ for(k? k--:0, j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); height[rank[i]] = k; } } int main() { freopen("data.in", "r", stdin); scanf("%s", str); int len = strlen(str); sortSA(str, sa, len+1, 255); calHeight(str, sa, len); printf("%s\n", str); for(int i = 1; i <= len; i++) printf("%d ", sa[i]); printf("\n"); for(int i = 1; i <= len; i++) printf("%d ", height[i]); printf("\n"); return 0; }