后缀数组

从网上找到了一个好的后缀数组模板,特发此随笔以保存,正好最近也在后缀数组。http://blog.csdn.net/u013480600/article/details/44763865

    #include<cstdio>  
    #include<cstring>  
    #include<algorithm>  
    using namespace std;  
      
    const int maxn=20000+1000;  
    struct SuffixArray  
    {  
        //保存原始字符串+‘\0’后形成的字符串  
        //即原始字符串在s中表示[0,n-2]范围,  
        //然后s[n-1]其实是人为添加的'\0'字符  
        char s[maxn];  
      
        //排名(后缀)数组,sa[i]==j表示字典序为i的后缀是后缀j  
        //其中i从0到n-1,j从0到n-1范围  
        int sa[maxn];  
      
        //名次数组,rank[i]==j表示后缀i的字典序排名为j  
        int rank[maxn];  
        int height[maxn];  
      
        //辅助数组用于x和y数组  
        int t1[maxn],t2[maxn];  
      
        //c[i]==j表示关键字<=i的关键字有j个  
        int c[maxn];  
      
        //s原始字符串+‘\0'字符后的长度  
        //由于添加了尾0,所以n一般都>=2的  
        int n;//n>=2,不能等于1,否则build_height()函数可能会出BUG  
        int dmin[maxn][20];  
        //m大于s[]数组出现的任意字符的int值  
        void build_sa(int m)  
        {  
            int i,*x=t1,*y=t2;  
      
            //预处理每个后缀的长度为1的前缀,求出x数组和sa数组  
            //此时x[i]==j表示第i个字符的绝对值(可以看成是名次数组)  
            //但有可能x[1]=2,且x[3]=2,说明1字符和3字符完全一样。  
            //此时算出的sa[i]==j表示当前长度为1的字符串的排名数组,  
            //排名数组值不会一样  
            //就算x[1]==x[3]==2,但是sa[1]=1,而sa[2]=3。  
            //即就算1号字符和3号字符是完全一样的,  
            //但是排名时第1名是1号字符,第2名才是3号字符  
            for(i=0;i<m;i++) c[i]=0;  
            for(i=0;i<n;i++) c[x[i]=s[i]]++;  
      
            //此时c[i]表示关键字<=i的关键字一共有c[i]个  
            for(i=1;i<m;i++) c[i]+=c[i-1];  
      
            //计算当前长度(1)的排名数组  
            for(i=n-1;i>=0;i--) sa[--c[x[i]]] = i;  
      
      
            //每轮循环开始前我们通过之前的计算得到了x[]和sa[]:  
            //每个后缀的长为k的前缀(即每个后缀的前[0,k-1]字符)的名次数组x[],  
            //我们还知道每个后缀的长为k的前缀的排名数组sa[],  
            //然后通过sa[]数组我们可以求得每个后缀的第[k,2*k-1]字符的排名数组y[],  
            //然后通过k字符的x[]与k字符的y[],  
            //我们可以求得每个后缀的长为2k的前缀字符串的sa[]排名数组  
            //然后通过该sa[]排名数组,和k字符的x数组,我们可以求得2k字符的x[]数组  
            //以上每轮的x[]名次数组都是可能有重复值大小的,但是sa[]值不会重复  
            //比如表示k个字符的x[1]=2,x[4]=2时,  
            //那么表示[1,k+1]字符串与[4,k+4]字符完全相同,且排名为2(最高排名为0)  
            //当哪轮求出的x[]数组正好由n个值(即所有值都不重复时)  
            //说明所有后缀已经排序完毕  
            for(int k=1;k<=n;k<<=1)  
            {  
                //先计算每个后缀的前缀的[k,2*k-1]字符的排名数组y  
                //即y是每个后缀的长为2k前缀的第二关键字  
                int p=0;  
      
                //y[p]==i表第二关键字为第p名的是后缀i  
                //由于当前处理的是每个后缀的前缀的[k,2*k-1]字符  
                //而后缀n-k到后缀n-1不存在第k个字符(想想是不是)  
                //所以他们的第二关键字的名字自然优先  
                for(i=n-k;i<n;i++) y[p++]=i;  
      
                //除了上面那些后缀不存在第二关键字  
                //x+k后缀的第1关键字排名-k 等于 x后缀的第2关键字排名  
                for(i=0;i<n;i++)if(sa[i]>=k) y[p++]=sa[i]-k;  
      
                //上面已经计算出了y[],(x[]数组上一轮已经算出)  
                //下面通过第1关键字x[]名次数组和第2关键字y[]排名数组  
                //计算综合后每个后缀的长2k前缀的sa[]数组  
                for(i=0;i<m;i++) c[i]=0;  
                for(i=0;i<n;i++) c[x[y[i]]]++;  
                for(i=1;i<m;i++) c[i]+=c[i-1];  
                for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];  
      
                //交换x和y,令y表示名次数组  
                //计算综合后每个后缀的长2k前缀的x[]数组  
                swap(x,y);  
                //此时p用来记录x[]数组中不同值的个数  
                p=1;x[sa[0]]=0;  
                for(i=1;i<n;i++)  
                    x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;  
                    //上面y[sa[i]+k]的sa[i]+k<=n-1,因为只要然后两个不同的后缀必然要分出大小  
                    //所以在他们y[sa[i]]==y[sa[i-1]],即这两个后缀的长k的第一关键字相同的情况下  
                    //他们必定还存在第二关键需要比较  
                if(p>=n) break;  
                m=p;  
            }  
        }  
      
        //此函数详解见刘汝佳<<训练指南>>P222  
        //height[i]表示sa[i-1]后缀与sa[i]后缀的最大公共前缀长度  
        //即表示排名i-1和排名i的后缀的最大公共前缀LCP长度  
        //所以height数组只有[1,n-1]是有效下标  
        void build_height()//n不能等于1,否则出BUG  
        {  
            int i,j,k=0;  
            for(i=0;i<n;i++)rank[sa[i]]=i;  
            for(i=0;i<n;i++)  
            {  
                if(k)k--;  
                j=sa[rank[i]-1];  
                while(s[i+k]==s[j+k]) k++;  
                height[rank[i]]=k;  
            }  
        }  
    void initMin()  
    {  
        for(int i=1;i<=n;i++) dmin[i][0]=height[i];  
        for(int j=1;(1<<j)<=n;j++)  
            for(int i=1;i+(1<<j)-1<=n;i++)  
                dmin[i][j]=min(dmin[i][j-1] , dmin[i+(1<<(j-1))][j-1]);  
    }  
    int RMQ(int L,int R)//取得范围最小值  
    {  
        int k=0;  
        while((1<<(k+1))<=R-L+1)k++;  
        return min(dmin[L][k] , dmin[R-(1<<k)+1][k]);  
    }  
    int LCP(int i,int j)//求后缀i和j的LCP最长公共前缀  
    {  
        int L=rank[i],R=rank[j];  
        if(L>R) swap(L,R);  
        L++;//注意这里  
        return RMQ(L,R);  
    }
    }sa;  

 

posted @ 2016-10-20 16:51  幻世沉溺  阅读(112)  评论(0编辑  收藏  举报