后綴數組之倍增算法

jopen 11年前發布 | 1K 次閱讀 C/C++ 算法

include<stdio.h>

include<string.h>

include<iostream>

using namespace std;

define MAXN 123123

char s[MAXN];

int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;

void build(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=0;i<m;i++) c[i]+=c[i-1];//其實這個時候每個數的名次已經有了

for(i=n-1;i>=0;i--)  sa[--c[x[i]]]=i;

/*最后一個for循環可能感覺莫名其妙可以改成下面兩個for循環可能就很容易的看出來了

for(i=n-1;i>=0;i--)

    rank[i]=--c[x[x[i]]];

for(i=n-1;i>=0;i--)

    sa[rank[i]]=i;

根據sa[rank[i]]=i這個定理把rank[i]為--c[x[x[i]]]所以把--c[x[x[i]]]帶入第二個

for循環就化為了上面計數排序的最后一個for循環

*/

for(int k=1;k<=n;k<<=1/*和k=k*2等價*/)

{

    int p=0;

    /*直接利用sa數組排序第二關鍵字也就是相當于一個兩位數的個位數,y保存的是第二關鍵字

    的sa[i]的值

    */

    for(i=n-k;i<n;i++)

        y[p++]=i;/*因為第二關鍵字超范圍,看做小于所有的第二關鍵字,所以他們的排名

        rank[n-k]...rank[n-1]的排名為0,1,2,....所以y[rank[n-k]]...y[rank[n-1]]的值位n-k...n-1

        根據sa[rank[i]]=i;化簡得rank[0]..rank[k]的值為n-k...n-1

    */

    for(i=0;i<n;i++)

        if(sa[i]>=k) y[p++]=sa[i]-k;

    /*因為這是排的第二關鍵字,當位于最后的后綴可能已經沒有第二個已經沒有第二個關鍵字了

      這個時候就把這樣的關鍵字都初始化為小于所有的第二關鍵字,而且,suffix(sa[0])<=suffix(sa[1])..

      當我們排第二關鍵字的時候第一關鍵字已經沒有用了,換句話說,前k個我們已經用不到了,也就是

      下標小于k的,也就是sa[i]小于k的,舉個例子baba這個字符串第一次算出來的sa數組為1 3 0 2,也就是

      suffix(1)<=suffix(3)<=suffix(0)<suffix(2)也就是a<=a<=b<=b而第二關鍵字為aba@這個@是小于所有的

      字符,而第一個a就沒有用了,也就是sa[k]=0的就沒有用了,因為sa[k]表示的是以0首字符為下標的

      的排名為k這個時候我們已經不再需要排小于k的下標了,但去掉小于k的sa[]后他們的sa[]的值是相對的

      如去掉0以后就剩下132了因為去掉了1個所以所有大于等于1的都得減1,同理,去掉k個就得減去k個

    */

    //基數排序第一個關鍵字x數組存的是相當于rank[],且y[i]相當于與第一關鍵字,可以手動模擬一下baba

    for(i=0;i<m;i++) c[i]=0;

    for(i=0;i<n;i++) c[x[y[i]]]++;

    for(i=0;i<m;i++) c[i]+=c[i-1];

    for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];

    /*

    可以換成下面的for更直觀一些rank1記錄的是rank的值,把y[i]看成一個下標

    for(i=0;i<m;i++) 

        rank[i]=x[y[i]];

    for(i=0;i<n;i++) c[i]=0;

    for(i=0;i<m;i++)

        c[rank[i]]++;

    for(i=0;i<m;i++)  c[i]+=c[i-1];

    for(i=n-1;i>=0;i--)

        rank1[y[i]]==--c[x[y[i]]];

    for(i=n-1;i>=0;i--)

        sa[rank1[y[i]]]=y[i];

    把rank1=--c[x[y[i]]]帶入得sa[--c[x[y[i]]]]=y[i];

    這個時候sa數組存的已經是倍增后的值但是這個時候rank[sa[i]]=i還不一定成立所以需要在下面進行驗證

    */

    //根據sa和y數組計算新的x數組(這時說的y是x數組和y數組交換后的y數組也就相當于rank數組)

    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;

    /*

    如果r[sa[i-1]]==r[sa[i]],那么,說明在以a,b為開始的l長度的字符串內肯定不包括@(這也是最后

    一個元素給@的原因),所以sa[i-1]+k,sa[i]+k都小于n-k,所以此處數組不會越界。若果不相等那么

    根據&&的短路特性后邊的sa[i]+k就不會判斷了

    */

}

}

int main()

{

return 0;

}

/*

dcba

aabb

baba

bababa

bbaa

aaab

*/

</pre>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!