關於後綴數組的一點想法

  後綴數組大概就是用後綴排名來搞一些事情,由於字符串中的每個子串均可看作某一後綴的前綴html

  可用倍增法求出後綴排名數組

 

 

1、數組意義(對於字符串 s)函數

  sa[i]:排名爲i的後綴的開頭在s中的位置優化

  height[i]:排名爲i的後綴和排名爲i-1的後綴的LCP(最長公共前綴)ui

  c[]:用於基數排序,統計前綴和spa

  rank[i]:以s[i]開頭的後綴的排名  顯然 rank[sa[i]]=i    sa[rank[i]]=i指針

 

2、求sa[]具體思路code

  1.用倍增法構造第1、第二關鍵詞,第一關鍵詞小的排在前,第一關鍵詞相同的 第二關鍵詞小的排在前。
htm

  2.優化:若是後綴長度枚舉到某一大小時,每一個後綴的排名彼此不一樣,那麼能夠直接退出,道理很顯然blog

 

3、求height[]具體思路

  先求出rank[i]

  看height[]的定義,知道應取suffix(sa[rank[i-1]])和suffix(i)的LCP

  只要找到suffix(sa[rank[i-1]])的開頭j,暴力枚舉即可

  此處有一個小優化(詳情參見http://www.cnblogs.com/LLGemini/p/4771235.html

     h[]即爲height[]

   對於i>1 且Rank[i]>1,必定有h[i]≥h[i-1]-1。(這條性質要好好理解!)

 

   證實:設suffix(k)是排在suffix(i-1)前一名的後綴,它們的最長公共前綴是h[i-1]。

 

              那麼suffix(k+1)將排在suffix(i)的前面(這裏要求h[i-1]>1,若是h[i-1]≤1,原式顯然成立)而且suffix(k+1)和suffix(i)的最長公共前綴是h[i-1]-1,

 

              因此suffix(i)和在它前一名的後綴的最長公共前綴至少是h[i-1]-1。

 

              按照h[1],h[2],……,h[n]的順序計算,並利用h 數組的性質,時間複雜度能夠降爲O(n)。

 

4、注意事項

  構建sa[]時,傳4個參進入函數,設原字符串爲s,設s的長度爲n,s中最大字符的大小爲m

  build_sa(s[],sa[],n+1,m+1)

  傳n+1而不傳n的緣由是在s的末尾補上了一個 「0」

  緣由:防止數組越界

     for(int i=1;i<n;i++)x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;

 

    若是 x[idx1]==x[idx2](注意idx1!=idx2), 說明以 idx1或 idx2 開頭的長度爲 len 的字符串確定不包括字符 x[n-1] , 因此調用變量 sa[idx1+len] 和 sa[idx2+len] 不會致使數組越界, 這樣就不須要作特殊判斷.

  

  完美解決了!

代碼:  

int sa[N],rk[N],h[N],c[N],r[N],wa[N],wb[N],sp[N],n,k; 

void get_sa(int *r,int *sa,int n,int m){
    int *x=wa,*y=wb;//都是輔助變量 
    for(int i=0;i<n;i++)c[x[i]=r[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;
    for(int k=1;k<=n;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;
        
        for(int i=0;i<m;i++)c[i]=0;
        for(int i=0;i<n;i++)c[x[i]]++;
        for(int i=1;i<m;i++)c[i]+=c[i-1];
        for(int i=n-1;~i;i--)sa[--c[x[y[i]]]]=y[i];
        
        swap(x,y);//x,y是指針,直接互換 
        p=1;x[sa[0]]=0;
        for(int i=1;i<n;i++)x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
        if(p>=n)break;
        m=p;//優化:最多有p個元素,下一次最大值爲p 
    }
}

void get_h(){
    int k=0,mh=-1;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++){
        if(k)k--;
        int j=sa[rk[i]-1];
        while(r[i+k]==r[j+k])k++;
        h[rk[i]]=k; 
    }
}

 

  

 

 

  補:還要隨時注意一個地方,對於整個串咱們補了一個'0'在串尾,那麼獲得的sa[]就必定是1~n爲原串,且傳入參數時, n和m都要+1

 

 

  2017-06-02 20:58:43

相關文章
相關標籤/搜索