後綴數組理解

參考PDF
cnblog
gitbook
計數排序和基數排序都用到了

第一步和每次求sa都用到了
基數是在第一關鍵字和第二關鍵字時用到
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;

y的內容是按照第二關鍵字排序結果(內容是下標),第一句是因爲小於長度的沒有第一關鍵字
,所以排序結果在最前面。第二句其實和rank差不多,只不過前幾個已經被佔了。
for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];需要理解y中的內容
x存儲的是rank
y存儲的是下標
因爲排序會出現sa相同,所以存儲rank的x會有相同的情況



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 SA(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])的最長公共前綴,也就是排名相鄰的兩個後綴的最長公共前綴。

h數組:h[i]=height[rank[i]]=Suffix(i)和在它前一名的後綴的最長公共前綴。


h數組的性質:h[i]>=h[i-1]-1。

證明:設Suffix(k)是排在Suffix(i-1)前一名的後綴,則它們的最長公共前綴就是h[i-1]。那麼Suffix(k+1)將排在Suffix(i)的前面。

a、若Suffix(k)與Suffix(i-1)的最長公共前綴<=1,即h[i-1]<=1,h[i]>=0顯然成立。

b、若Suffix(k)與Suffix(i-1)的最長公共前綴>=2,Suffix(k)與Suffix(i-1)同時去掉首字符得到Suffix(k+1)與Suffix(i),則Suffix(k+1)排在Suffix(i)的前面,且Suffix(k+1)與Suffix(i)的最長公共前綴=h[i-1]-1。設Suffix(t)是排在Suffix(i)前一名的後綴,則它們的最長公共前綴就是h[i],那麼Suffix(t)=Suffix(k+1)或者Suffix(t)排在Suffix(k+1)前面,則h[i]>=h[i-1]-1。

int rank[MAXN],height[MAXN];
void Height(int *r,int *sa,int n)
{
    int i,j,k;
    for(i=1;i<=n;i++) 
        rank[sa[i]]=i;
    for(i=k=0;i<n;height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    //若k>0,從k-1開始找最長公共前綴。
    return;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章