后缀数组(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;
}
posted @ 2012-09-21 22:20  fCarver7  阅读(223)  评论(0编辑  收藏  举报