• POJ3693


    Portal

    Description

    给出一个字符串(s),求(s)的一个循环子串,其循环次数最多。

    Solution

    容易知道(left lfloor dfrac{lcp(s_{i..n},s_{i+L..n})}{L} ight floor+1)就等于从(s_i)开始的长度为(L)的串的循环次数。

    [egin{align*} s quad& cababacbacbacbaca \ i=2 quad& .aba............. \ i+L=4 quad& ...aba........... \ i=4 quad& .....acbacbac.... \ i+L=7 quad& ........acbacbac. \ end{align*}]

    枚举循环节的长度(L)。如果有一个长度为(L)的子串连续出现,那么它至少会覆盖(s_1,s_{L+1},s_{2L+1}...)中的连续两个。那么我们记录(s_{tL+1})(s_{(t+1)L+1})向前匹配的长度为(x),向后匹配的长度为(y)。那么循环串可能以(s_{i-x}..s_{i-x+(y-x)\%L})中任意一个作为开头循环(dfrac{x+y}{L}+1)次,用RMQ求出区间最小的(rnk)即可更新答案。

    Code

    //Maximum repetition substring
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int const N=1e5+10;
    int n;
    char s[N];
    int sa[N],rnk[N<<1],h[N];
    int cnt[N],tmp[N],rnk1[N<<1];
    int rmq[N<<1][20];
    void getSA()
    {
        memset(rnk,0,sizeof rnk);
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++) cnt[s[i]]=1;
        for(int i=1;i<=256;i++) cnt[i]+=cnt[i-1];
        for(int i=1;i<=n;i++) rnk[i]=cnt[s[i]];
        for(int L=1,k=0;k<n;L<<=1)
        {
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[i+L]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) tmp[cnt[rnk[i+L]]--]=i;
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[tmp[i]]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
            k=0; memcpy(rnk1,rnk,sizeof rnk1);
            for(int i=1;i<=n;i++)
            {
                if(rnk1[sa[i]]!=rnk1[sa[i-1]]||rnk1[sa[i]+L]!=rnk1[sa[i-1]+L]) k++;
                rnk[sa[i]]=k;
            }
        }
        for(int i=1,k=0;i<=n;i++)
        {
            if(rnk[i]==1) {h[1]=k=0; continue;}
            if(k) k--;
            while(s[i+k]==s[sa[rnk[i]-1]+k]) k++;
            h[rnk[i]]=k;
        }
        memset(rmq,0,sizeof rmq);
        for(int i=1;i<=n;i++) rmq[i][0]=h[i];
        for(int k=1;(1<<k)<=n;k++)
            for(int i=1;i<=n;i++)
                rmq[i][k]=min(rmq[i][k-1],rmq[i+(1<<k-1)][k-1]);
    }
    int lcp(int i,int j)
    {
        if(i==j) return n-i+1;
        int x=rnk[i],y=rnk[j],t=0;
        if(x>y) swap(x,y); x++;
        while((1<<t)<=y-x+1) t++; t--;
        return min(rmq[x][t],rmq[y-(1<<t)+1][t]);
    }
    int ansS,ansT,ansL;
    int main()
    {
        for(int task=1;true;task++)
        {
    
        memset(s,0,sizeof s);
        scanf("%s",s+1); if(s[1]=='#') break;
        n=strlen(s+1); getSA();
        ansS=1,ansT=1,ansL=n;
        for(int L=1;L<=n;L++)
            for(int i=1;i+L<=n;i+=L)
            {
                int j=i+L,len=lcp(i,j);
                for(int k=0;i-k>0&&s[i-k]==s[j-k]&&k<L;k++)
                {
                    int t=(len+k)/L+1;
                    if(t>ansT) ansS=i-k,ansT=t,ansL=L;
                    else if(t==ansT&&rnk[i-k]<rnk[ansS]) ansS=i-k,ansT=t,ansL=L;
                }
            }
        printf("Case %d: ",task);
        for(int i=1;i<=ansT*ansL;i++) printf("%c",s[ansS+i-1]);
        puts("");
    
        }
        return 0;
    }
    

    P.S.

    我没写RMQ,也能AC原题;但是(aaaa..aa)就能hack我这个程序。

  • 相关阅读:
    Java单例模式
    svn 清理失败 (clean up 失败) 的解决方法
    linux chrome 安装过程记录
    docker
    linux-cat-grep
    linux-批量结束进程
    linux-压缩与解压缩(gz,zip,tar,jar,war)
    git-服务器地址切换
    linux-tomcat连接数查询
    git-无法add文件的问题
  • 原文地址:https://www.cnblogs.com/VisJiao/p/POJ3693.html
Copyright © 2020-2023  润新知