后缀数组

用倍增思想求\(sa_{k}[],rk_{k}[] -> sa_{2k}[],rk_{2k}[]\)
用基数排序,排序二元组\((rk[i],rk[i+k])\)
复杂度\(O(N\log{N})\)

#include<bits/stdc++.h>
const int N=1000010;
char s[N];
int n,m,num;
int sa[N],rk[N],bac[N],y[N],tmp[N];//sa[i]数组表示后缀排名为i的位置,rk[i]表示后缀[i..n]的排名
int height[N];//height[i]表示rk为i的后缀与rk为i-1的后缀的LCP
void getsa(){
    //rk[i]表示位置i的第一关键字(排名)
    //二元组(rk[i],rk[i+k])
    //初始化基数排序(单字符,其实是单元)
    for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++;//bac[i]表示第一关键字小于i的个数
    for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐个统计,再求前缀和,相当于用桶计数
    for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i;//在第二关键字有序的情况下,对位置按照第一关键字的bac数组,逐个附排名 
    for(int k=1;k<=n;k<<=1){
        num=0;//排名的数量,初始化为单个字符的ASCLL码上限,m=ascll('z')=122
        //y[i]表示第二关键字排名为i的二元组的第一关键字位置(i)
        for(int i=n-k+1;i<=n;i++)y[++num]=i;//没有第二关键字的排名最靠前
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;//利用已有的sa_(k/2)数组,rk靠前的且有第一关键字的,将第一关键字位置sa_(k/2)[i]-k放入y
        //在上一轮中,rk_k数组已经求好了,倍增求解,
        //接下来利用基数排序,求出sa_k
        for(int i=1;i<=m;i++)bac[i]=0;
        for(int i=1;i<=n;i++)bac[rk[i]]++;//bac[i]表示第一关键字小于i的个数
        for(int i=2;i<=m;i++)bac[i]+=bac[i-1];//故就先逐个统计,再求前缀和,相当于用桶计数
        for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i];//在第二关键字有序的情况下,对位置按照第一关键字的bac数组,逐个附排名 
        memcpy(tmp,rk,sizeof(tmp));//用tmp存一下rk_(k/2),在求rk_k时仍会用到
        //其实就是利用sa_k和rk_(k/2)数组求rk_k
        rk[sa[1]]=1;num=1;
        for(int i=2;i<=n;i++){
            if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num;
            else rk[sa[i]]=++num;
        }
        if(num==n)break;//已经有了n种排名
        m=num;//m表示排名的数量(在桶排中为值域)
    }
}
void geth(){
    //rk[i]=rk1,sa[rk1-1]=x;
    //rk[i-1]=rk2,as[rk2-1]=y;
    //height[rk1]=lcp(s[i..],s[x...]);
    //height[rk2]=lcp[s[i-1..],s[y...]);
    //lcp(s[i..],s[x..])>=lcp(s[i-1...],s[y...])
    for(int i=1;i<=n;i++){
        int x=sa[rk[i]-1];
        int k=std::max(0,height[rk[i-1]]-1);
        while(s[i+k]==s[x+k])++k;
        height[rk[i]]=k;
    }
}
int main(){
    scanf("%s",s+1);n=strlen(s+1);m=122;
    getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]);
    printf("\n");
    geth();for(int i=2;i<=n;i++)printf("%d ",height[i]);
    return 0;
}

无注释版:

#include<bits/stdc++.h>
const int N=1000010;
char s[N];
int n,m,num;
int sa[N],rk[N],bac[N],y[N],tmp[N];
void getsa(){
    for(int i=1;i<=n;i++)bac[rk[i]=s[i]]++;
    for(int i=2;i<=m;i++)bac[i]+=bac[i-1];
    for(int i=n;i>=1;i--)sa[bac[rk[i]]--]=i;
    for(int k=1;k<=n;k<<=1){
        num=0;
        for(int i=n-k+1;i<=n;i++)y[++num]=i;
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
        for(int i=1;i<=m;i++)bac[i]=0;
        for(int i=1;i<=n;i++)bac[rk[i]]++;
        for(int i=2;i<=m;i++)bac[i]+=bac[i-1];
        for(int i=n;i>=1;i--)sa[bac[rk[y[i]]]--]=y[i];
        memcpy(tmp,rk,sizeof(tmp));
        rk[sa[1]]=1;num=1;
        for(int i=2;i<=n;i++){
            if(tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k])rk[sa[i]]=num;
            else rk[sa[i]]=++num;
        }
        if(num==n)break;
        m=num;
    }
}
int main(){
    scanf("%s",s+1);n=strlen(s+1);m=122;
    getsa();for(int i=1;i<=n;i++)printf("%d ",sa[i]);
    return 0;
}
posted @ 2020-07-23 19:14  行zzz  阅读(130)  评论(0编辑  收藏  举报