• hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq


    http://acm.hdu.edu.cn/showproblem.php?

    pid=4691

    去年夏天,更多的学校的种族称号。当时,没有后缀数组

    今天将是,事实上,自己的后缀阵列组合rmq或到,但是,题意理解的一个问题,再折腾了很长时间,,,,

    此处简单解释下题目例子吧,希望对读者有帮助  以最后一组数据为例

    myxophytamyxopodnabnabbednabbingnabit

    6

    0 9

    9 16

    16 19

    19 25

    25 32

    32 37

    前两行不解释,题目叙述非常清楚

    从第三行,0 9 指的是第一个字符串是从第一行的字符串的0-9 左闭右开,

    下面5行同样

    继续看题目的正文叙述的例子

    那么压缩之后的第一个就是“0空格以及前9个字符外加一个换行”一共12个。下面几行相同的算法

    注意假设公共前缀长度是24,那么按两个单元存储,这就是我写的Weishu函数的作用


    上代码:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cstdlib>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN =100000+20;
    
    int n,k;//n=strlen(s);
    int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
    char s[MAXN];
    
    /*使用Rank对sa排序*/
    bool cmpSa(int i, int j)
    {
        if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
        else
        {   /*以下的Rank[t],已经是以t开头长度小于等于k/2的。
            sa[i]的名次,仅仅是以i开头的后缀,而长度不同*/
            int ri = i+k <=n? Rank[i+k]:-1;
            int rj = j+k <= n ? Rank[j+k]:-1;
            return ri <rj;
        }
    }
    
    /*计算SA*/
    void consa(char *s, int *sa)
    {
        for(int i=0;i<=n;i++){
            sa[i]=i;Rank[i] = i < n?s[i]:-1;
        }
    
        for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始,由于0*2=0*/
        {
            sort(sa,sa+n+1,cmpSa);
            tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
            for(int i=1;i<=n;i++){
                tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
            }
            for(int i=0;i<=n;i++){
                Rank[i] = tmp[i];
            }
        }
    }
    
    void construct_lcp(char *s,int *sa,int *lcp)
    {
        //n=strlen(s);
        for(int i=0; i<=n; i++)Rank[sa[i]]=i;
    
        int h=0;
        lcp[0]=0;
        for(int i=0;i<n;i++)
        {
            int j=sa[Rank[i]-1];
    
            if(h>0)h--;
            for(; j+h<n && i+h<n; h++)
            {
                if(s[j+h]!=s[i+h])break;
            }
            lcp[Rank[i]-1]=h;
        }
    }
    
    void InitRMQ(int nn)
    {
        int i,j;
        for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
        for(i=0;i<nn;i++)st[i][0]=lcp[i];
        ///////////////////////////////
       // for(int i=0;i<nn;i++)
       //{
      //      printf("%s i=%d sa=%d rank=%d lcp=%d
    ",s+sa[i],i,sa[i],Rank[i],lcp[i]);
      //      printf("||||||||%s lcp[rank]=%d
    ",s+i,lcp[Rank[i]]);
      //  }
        ////////////////////////////////
    
        int k=int( log(double(nn))/log(2.0)+1 );
        for(j=1;j<k;j++)
            for(i=0;i<nn;i++)
            {
                if(i+d[j-1]-1<nn)
                {
                    st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
                }
                else break;
            }
    }
    
    int weishu(int n)
    {
        if(n<10)return 1;
        int ans=0;
        while(n)
        {
            n/=10;
            ans++;
        }
        return ans;
    }
    
    int main()
    {
        //freopen("hdu4691.txt","r",stdin);
        long long ansb,ansa;
        int kk;
        while(~scanf("%s",s))
        {
            ansb=ansa=0;
            n=strlen(s);
            consa(s,sa);
            construct_lcp(s,sa,lcp);
    
            InitRMQ(n+1);
            int num,l,r,x,y;
            scanf("%d",&num);
            int last=0,lastlen=0;
            scanf("%d%d",&l,&r);
            ansb+=r-l+1;
            ansa+=r-l+3;
            lastlen=r-l;
            last=l;
            for(int i=1;i<num;i++)
            {
                scanf("%d%d",&l,&r);
                ansb+=r-l+1;
                ansa+=r-l;
                x=min(Rank[last],Rank[l]),y=max(Rank[last],Rank[l])-1;
                kk = int( log(double(y-x+1))/log(2.0) );
                int ret;
                if(l == last)ret=n-l;
                else ret=min(st[x][kk], st[y-d[kk]+1][kk]) ;
                ret=min(ret, min(lastlen,r-l));
                ansa =ansa-ret+weishu(ret)+2;
                last=l;
                lastlen=r-l;
            }
            printf("%I64d %I64d
    ",ansb,ansa);
        }
        return 0;
    }
    

    再加一个rmq+后缀数组求最长公共前缀的模板吧

    (事实上还没有測试,遇到题在測试)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cstdlib>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN =100000+20;
    
    int n,k;//n=strlen(s);
    int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
    char s[MAXN];
    
    /*使用Rank对sa排序*/
    bool cmpSa(int i, int j)
    {
        if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
        else
        {   /*以下的Rank[t],已经是以t开头长度小于等于k/2的,
            sa[i]的名次。仅仅是以i开头的后缀,而长度不同*/
            int ri = i+k <=n? Rank[i+k]:-1;
            int rj = j+k <= n ? Rank[j+k]:-1;
            return ri <rj;
        }
    }
    
    /*计算SA*/
    void consa(char *s, int *sa)
    {
        for(int i=0;i<=n;i++){
            sa[i]=i;Rank[i] = i < n?s[i]:-1;
        }
    
        for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始。由于0*2=0*/
        {
            sort(sa,sa+n+1,cmpSa);
            tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
            for(int i=1;i<=n;i++){
                tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
            }
            for(int i=0;i<=n;i++){
                Rank[i] = tmp[i];
            }
        }
    }
    
    void construct_lcp(char *s,int *sa,int *lcp)
    {
        //n=strlen(s);
        for(int i=0; i<=n; i++)Rank[sa[i]]=i;
    
        int h=0;
        lcp[0]=0;
        for(int i=0;i<n;i++)
        {
            int j=sa[Rank[i]-1];
    
            if(h>0)h--;
            for(; j+h<n && i+h<n; h++)
            {
                if(s[j+h]!=s[i+h])break;
            }
            lcp[Rank[i]-1]=h;
        }
    }
    
    void InitRMQ(int nn)
    {
        int i,j;
        for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
        for(i=0;i<nn;i++)st[i][0]=lcp[i];
        int k=int( log(double(nn))/log(2.0)+1 );
        for(j=1;j<k;j++)
            for(i=0;i<nn;i++)
            {
                if(i+d[j-1]-1<nn)
                {
                    st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
                }
                else break;
            }
    }
    
    int ansLcp(int a, int b)
    {
        int kk;
        if(a == b)return n-a;//n是整个字符串的长度
        x=min(Rank[a],Rank[b]),y=max(Rank[a],Rank[b])-1;
        kk = int( log(double(y-x+1))/log(2.0) );
        return min(st[x][kk], st[y-d[kk]+1][kk]) ;
    }
    
    int Query(int Q)//Q次询问
    {
        int ans;
        for(int i=0;i<Q;i++)
        {
            scanf("%d%d",&a,&b);
            ans=ansLcp(a,b);
        }
    }
    


    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    bzoj 1407: [Noi2002]Savage【扩展欧几里得+中国剩余定理】
    bzoj 2142: 礼物【中国剩余定理+组合数学】
    BSGS
    bzoj 2242: [SDOI2011]计算器【扩展欧几里得+快速幂+BSGS】
    bzoj 3122: [Sdoi2013]随机数生成器【BSGS】
    bzoj 3239: Discrete Logging && 2480: Spoj3105 Mod【BSGS】
    bzoj 1180: [CROATIAN2009]OTOCI【LCT】
    51nod 1122 机器人走方格 V4 【矩阵快速幂】
    51nod 1120 机器人走方格 V3 【卡特兰数+卢卡斯定理+组合数】
    51nod 1119 机器人走方格 V2 【组合数学】
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4671842.html
Copyright © 2020-2023  润新知