zoukankan      html  css  js  c++  java
  • 后缀数组

    用倍增思想求(sa_{k}[],rk_{k}[] -> sa_{2k}[],rk_{2k}[])
    用基数排序,排序二元组((rk[i],rk[i+k]))
    复杂度(O(Nlog{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("
    ");
        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;
    }
    
  • 相关阅读:
    HDU 3401 Trade
    POJ 1151 Atlantis
    HDU 3415 Max Sum of MaxKsubsequence
    HDU 4234 Moving Points
    HDU 4258 Covered Walkway
    HDU 4391 Paint The Wall
    HDU 1199 Color the Ball
    HDU 4374 One hundred layer
    HDU 3507 Print Article
    GCC特性之__init修饰解析 kasalyn的专栏 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/hangzz/p/13368161.html
Copyright © 2011-2022 走看看