學習筆記----字尾陣列

畫船聽雨發表於2015-01-22

學習資料:IOI2009國家集訓隊論文——《字尾陣列》

論文裡面寫的比較清晰了,但是程式碼裡面沒有解釋,又從網上找到了一份程式碼的註釋,解釋的挺好的

地址:http://www.cnblogs.com/Lyush/p/3233573.html

這裡是程式碼模板:

倍增演算法實現的,效率很高。

const int maxn = 10010;
int wa[maxn], wb[maxn], wv[maxn], ws1[maxn];
int cmp(int *r, int a, int b, int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i, j, p, *x = wa,*y = wb;
    // 下面四行是對第一個字母的一個基數排序:基數排序其實就是記錄前面有多少個位置被佔據了
    for(i = 0; i<m; i++) ws1[i]=0; // 將統計字元數量的陣列清空
    for(i = 0; i<n; i++) ws1[x[i]=r[i]]++; // 統計各種字元的個數
    for(i = 1; i<m; i++) ws1[i]+=ws1[i-1]; // 進行一個累加,因為前面的小字符集對後面字元的排位有位置貢獻
    for(i = n-1; i>=0; i--) sa[--ws1[x[i]]]=i; // 根據位置來排序,sa[x] = i,表示i位置排在第x位
    // wa[x[i]]就是字符集0-x[i]共有多少字元佔據了位置,減去自己的一個位置剩下的就是自己的排名了,排名從0開始
    // 排名過程中主要的過程是對於處於相同字元的字元的排序,因為改變wa[x[i]]值得只會是本身,小於該字元的貢獻值
    // 是不變的,對於第一個字元相同的依據是位置關係,在後面將看到通過第二個關鍵字來確定相同字元的先後關係


    // 這以後的排序都是通過兩個關鍵字來確定一個串的位置,也即倍增思想
    // 通過將一個串分解成兩部分,而這兩部分的位置關係我們都已經計算出來
    for(j = 1, p = 1; p<n; j*=2, m=p)
    {
        for(p = 0, i = n-j; i<n; i++) y[p++]=i; // 列舉的串是用於與i位置的串進行合併,由於i較大,因為匹配的串為空串
        // 由於列舉的是長度為j的串,那麼i位置開始的串將湊不出這個長度的串,因此第二關鍵字應該最小,這其中位置靠前的較小
        for(i = 0; i<n; i++)
            if(sa[i]>=j) y[p++]=sa[i]-j; // sa[i]-j開頭的串作為第二關鍵字與編號為sa[i]的串匹配,sa[i]<j的串不用作為第二關鍵字來匹配
        for(i = 0; i<n; i++) wv[i]=x[y[i]]; // 取出這些位置的第一關鍵字
        for(i = 0; i<m; i++) ws1[i]=0;
        for(i = 0; i<n; i++) ws1[wv[i]]++;
        for(i = 1; i<m; i++) ws1[i]+=ws1[i-1];
        for(i = n-1; i>=0; i--) sa[--ws1[wv[i]]]=y[i]; // 按照第二關鍵字進行第一關鍵字的基數排序
        for(swap(x,y),p=1,x[sa[0]]=0,i=1; i<n; i++) // 對排好序的sa陣列進行一次字符集縮小、常數優化
            x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}

int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n) // 這裡的n是原串的本來長度,即不包括新增的0
{
    int i,j,k=0;
    for(i = 1; i<=n; i++) rank[sa[i]]=i; // 有字尾陣列得到名次陣列,排名第0的字尾一定是新增的0
    for(i = 0; i<n; height[rank[i++]]=k) // 以 i 開始的字尾總能夠從以 i-1 開始的字尾中繼承 k-1 匹配項出來
        for(k?k--:0, j=sa[rank[i]-1]; r[i+k] == r[j+k]; k++); // 進行一個暴力的匹配,但是整個演算法的時間複雜度還是O(n)的
    return;
}

int main()
{

    return 0;
}


模板:


const int maxn = 10010;
int wa[maxn], wb[maxn], wv[maxn], ws1[maxn];
int cmp(int *r, int a, int b, int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i, j, p, *x = wa,*y = wb;
    
    for(i = 0; i<m; i++) ws1[i]=0; 
    for(i = 0; i<n; i++) ws1[x[i]=r[i]]++; 
    for(i = 1; i<m; i++) ws1[i]+=ws1[i-1]; 
    for(i = n-1; i>=0; i--) sa[--ws1[x[i]]]=i;

    for(j = 1, p = 1; p<n; j*=2, m=p)
    {
        for(p = 0, i = n-j; i<n; i++) y[p++]=i; 
       
        for(i = 0; i<n; i++)
            if(sa[i]>=j) y[p++]=sa[i]-j; 
        for(i = 0; i<n; i++) wv[i]=x[y[i]];
        for(i = 0; i<m; i++) ws1[i]=0;
        for(i = 0; i<n; i++) ws1[wv[i]]++;
        for(i = 1; i<m; i++) ws1[i]+=ws1[i-1];
        for(i = n-1; i>=0; i--) sa[--ws1[wv[i]]]=y[i]; 
        for(swap(x,y),p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}

int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n) 
{
    int i,j,k=0;
    for(i = 1; i<=n; i++) rank[sa[i]]=i; 
    for(i = 0; i<n; height[rank[i++]]=k) 
        for(k?k--:0, j=sa[rank[i]-1]; r[i+k] == r[j+k]; k++); 
    return;
}

int main()
{

    return 0;
}


相關文章