【數據結構】倍增算法 - 后綴數組



后綴數組的倍增算法

后綴數組


算法介紹

  先根據字符串中字符的出現情況,給每一種字符一個對應的排名(從1開始),作為第一次排序的結果

  其后每一次,每個位置以當前排名作為主關鍵詞,從1開始倍增步數,將對應的位置排名作為第二關鍵詞

  於是根據主關鍵詞與副關鍵詞繼續給定排名,作為當次排序的結果

  如果加上倍增的步數后超出了字符串長度Len,則副關鍵詞排名為 0

  如此循環,直到第一個位置加上倍增步數后超出字符串長度為止,算作算法結束,此時得到的排序結果即為sa數組

  總共排序次數為 logn

  若排序使用快排O(nlogn),則總時間復雜度為O(nlog2n)

  若使用基數排序,則可將排序復雜度降至O(n),總復雜度降為O(nlogn)

  經典基數排序圖示:

  pic




模板

完全看懂太難了,學會用法后復制粘貼吧

const int N=100050;

int xx[N],yy[N],cnt[N]; //緩存數組
int sa[N],rk[N],height[N]; //結果
char str[N]; //字符串

倍增求sa

void getSA_DA(int n,int M) //n=length+1,M表示待處理字符串最大可能擁有的字符種類
{
    int i,j,p,*x=xx,*y=yy;
    for(i=0;i<M;i++)
        cnt[i]=0;
    for(i=0;i<n;i++)
        cnt[x[i]=str[i]]++;
    for(i=1;i<M;i++)
        cnt[i]+=cnt[i-1];
    for(i=n-1;i>=0;i--)
        sa[--cnt[x[i]]]=i; //根據原字符串求出每個字符的初始排名
    for(j=1,p=1;p<n;j<<=1,M=p) //倍增步數j
    {
       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<M;i++)
           cnt[i]=0;
       for(i=0;i<n;i++)
           cnt[x[y[i]]]++;
       for(i=1;i<M;i++)
           cnt[i]+=cnt[i-1];
       for(i=n-1;i>=0;i--)
           sa[--cnt[x[y[i]]]]=y[i];
       swap(x,y); //交換兩數組指針(交替使用)
       p=1;x[sa[0]]=0;
       for(i=1;i<n;i++)
           if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])
               x[sa[i]]=p-1;
           else
               x[sa[i]]=p++;
    }
}

  使用指針來指向緩存數組,便於進行指針交替

  需要注意的是,這里的 n == len+1 ,字符串最后一個位置置零處理(讀入自動添加'\0'結束符時可以略過)

  M 表示字符串擁有的字符最大種類數

  此時求出的sa數組的內容編號從0開始,內容+1才是所求編號


求rk與height數組

void getHeight(int n) //n=length
{
    int i,j,k=0;
    for(i=1;i<=n;i++) //導出rk數組
        rk[sa[i]]=i;
    for(i=0;i<n;)
    {
        if(k)k--; //根據關系,本次LCP最小值為上一次的LCP-1
        j=sa[rk[i]-1];
        while(str[i+k]==str[j+k]) //在k的基礎上繼續遍歷判斷LCP
            k++;
        height[rk[i++]]=k;
    }
    for(i=n;i;i--) //rk后移,sa增加編號
        rk[i]=rk[i-1],sa[i]++;
}

  這里的 n==len ,先 O(n) 由sa求出rk數組,再根據關系遍歷字符串求出height數組

  由於前面處理范圍為 [0,len-1] ,排名編號從0開始,故最后將rk數組整體后移一位

  又因為sa內容編號從0開始,故sa自增


DA算法完整模板

#include<bits/stdc++.h>
using namespace std;
const int N=100050;

int xx[N],yy[N],cnt[N];
int sa[N],rk[N],height[N];
char str[N];

void getSA_DA(int n,int M)
{
    int i,j,p,*x=xx,*y=yy;
    for(i=0;i<M;i++)
        cnt[i]=0;
    for(i=0;i<n;i++)
        cnt[x[i]=str[i]]++;
    for(i=1;i<M;i++)
        cnt[i]+=cnt[i-1];
    for(i=n-1;i>=0;i--)
        sa[--cnt[x[i]]]=i;
    for(j=1,p=1;p<n;j<<=1,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<M;i++)
            cnt[i]=0;
        for(i=0;i<n;i++)
            cnt[x[y[i]]]++;
        for(i=1;i<M;i++)
            cnt[i]+=cnt[i-1];
        for(i=n-1;i>=0;i--)
            sa[--cnt[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])
                x[sa[i]]=p-1;
            else
                x[sa[i]]=p++;
    }
}
void getHeight(int n)
{
    int i,j,k=0;
    for(i=1;i<=n;i++)
        rk[sa[i]]=i;
    for(i=0;i<n;)
    {
        if(k)k--;
        j=sa[rk[i]-1];
        while(str[i+k]==str[j+k])
            k++;
        height[rk[i++]]=k;
    }
    for(i=n;i;i--)
        rk[i]=rk[i-1],sa[i]++;
}
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    getSA_DA(len+1,128);
    getHeight(len);
    
    /*
    for(int i=1;i<=len;i++)
        printf("%d ",sa[i]);
    putchar('\n');
    for(int i=1;i<=len;i++)
        printf("%d ",rk[i]);
    putchar('\n');
    for(int i=1;i<=len;i++)
        printf("%d ",height[i]);
    putchar('\n');
    */
    return 0;
}

行數壓縮后模板

#include<bits/stdc++.h>
using namespace std;
const int N=100050;
int xx[N],yy[N],cnt[N];
int sa[N],rk[N],height[N];
char str[N];
void getSA_DA(int n,int M){
    int i,j,p,*x=xx,*y=yy;
    for(i=0;i<M;i++)cnt[i]=0;
    for(i=0;i<n;i++)cnt[x[i]=str[i]]++;
    for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
    for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
    for(j=1,p=1;p<n;j<<=1,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<M;i++)cnt[i]=0;
        for(i=0;i<n;i++)cnt[x[y[i]]]++;
        for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
        for(i=n-1;i>=0;i--)sa[--cnt[x[y[i]]]]=y[i];
        for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
    }
}
void getHeight(int n){
    int i,j,k=0;
    for(i=1;i<=n;i++)rk[sa[i]]=i;
    for(i=0;i<n;height[rk[i++]]=k)
        for(k?k--:0,j=sa[rk[i]-1];str[i+k]==str[j+k];k++);
    for(i=n;i;i--)rk[i]=rk[i-1],sa[i]++;
}
int main(){
    scanf("%s",str);
    int len=strlen(str);
    getSA_DA(len+1,128);
    getHeight(len);
    
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM