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

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

        #include<cstdio>  
        #include<cstring>  
        #include<algorithm>  
        using namespace std;  
          
        const int maxn=20000+1000;  
        struct SuffixArray  
        {  
            //保存原始字符串+‘’后形成的字符串  
            //即原始字符串在s中表示[0,n-2]范围,  
            //然后s[n-1]其实是人为添加的''字符  
            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,所以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;  
  • 相关阅读:
    C# 中字符串转换成日期
    c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.ExitThread(); System.Environment.Exit(0);
    c#获取程序版本号
    分分钟用上C#中的委托和事件
    【转载】C# 中的委托和事件(详解:简单易懂的讲解)
    C#什么时候需要使用构造函数
    15、生命周期-BeanPostProcessor-后置处理器
    13、生命周期-InitializingBean和DisposableBean
    11、组件注册-使用FactoryBean注册组件
    12、生命周期-@Bean指定初始化和销毁方法
  • 原文地址:https://www.cnblogs.com/137033036-wjl/p/5981499.html
Copyright © 2011-2022 走看看