zoukankan      html  css  js  c++  java
  • SPOJ 687 REPEATS

    题意

    给定字符串,求重复次数最多的连续重复子串

    思路

    后缀数组的神题
    让我对着题解想了快1天
    首先考虑一个暴力,枚举循环串的长度l,然后再枚举每个点i,用i和i+l匹配,如果匹配长度是L,这个循环串就出现了(lfloorfrac{L}{l} floor+1)
    但是这样显然是n^2的
    根本过不去
    考虑一个常见的思路,间隔某个长度设置关键点,由经过关键点的个数确定贡献,如果间隔l放置关键点,那么每个长为l的循环串应该都会经过一个关键点且没有一个循环串会经过两个关键点,然后枚举关键点,复杂度就是调和级数级别((O(nlog n)))了
    但是注意一种特殊情况,枚举关键点判断的是从关键点位置开头的字符串的最长长度,有可能会出现一个字符串可以向左移动x位,依然满足条件,这样的字符串显然更优,出现这种情况时,l不能整除L,应该向左移动i-(l-L%l)位再次匹配,如果匹配长度大于l,证明能多出一个循环的字符串,注意只可能多出1个,多出两个及以上的情况在前面的枚举中应该已经被计算过了
    这题就解决了

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define int long long
    using namespace std;
    const int MAXlog = 20;
    const int MAXN = 100000;
    const int INF = 0x3f3f3f3f;
    struct Node{
        int pos,r[2];
    }x[MAXN],midx[MAXN];
    int barrel[MAXN],sa[MAXN],height[MAXN],ranks[MAXN],ST[MAXN][MAXlog],n;
    char s[MAXN];
    int c_sort(int n,int lim){
        for(int i=0;i<2;i++){
            memset(barrel,0,sizeof(barrel));
            for(int j=1;j<=n;j++)
                barrel[x[j].r[i]]++;
            for(int j=1;j<=lim;j++)
                barrel[j]+=barrel[j-1];
            for(int j=n;j>=1;j--)
                midx[barrel[x[j].r[i]]--]=x[j];
            for(int j=1;j<=n;j++)
                x[j]=midx[j];
        }
        int cnt=1;
        ranks[x[1].pos]=1;
        for(int i=2;i<=n;i++)
            if(x[i].r[0]==x[i-1].r[0]&&x[i].r[1]==x[i-1].r[1])
                ranks[x[i].pos]=cnt;
            else
                ranks[x[i].pos]=++cnt;
        return cnt;
    }
    void cal_sa(int n){
        for(int i=1;i<=n;i++)
            x[i]=(Node){i,s[i],0};
        int cnt=c_sort(n,255);
        for(int i=1;cnt<n;i<<=1){
            for(int j=1;j<=n;j++)
                x[j]=(Node){j,(i+j<=n)?ranks[i+j]:0,ranks[j]};
            cnt=c_sort(n,cnt);
        }
        for(int i=1;i<=n;i++)
            sa[ranks[i]]=i;
        for(int i=1,j=0,k;i<=n;height[ranks[i++]]=j)
            for(j?j--:0,k=sa[ranks[i]-1];s[i+j]==s[j+k];j++);
    }
    void init(void){
        memset(sa,0,sizeof(sa));
        memset(ranks,0,sizeof(ranks));
        memset(height,0,sizeof(height));
        memset(ST,0,sizeof(ST));
    }
    void init_ST(void){
        for(int i=1;i<=n;i++)
            ST[i][0]=height[i];
        for(int i=1;i<MAXlog;i++)
            for(int j=1;j<=n;j++)
                ST[j][i]=min(ST[j][i-1],ST[min(j+(1<<(i-1)),n)][i-1]);
    }
    int LCP(int l,int r){
        l=ranks[l];
        r=ranks[r];
        if(l==r)
            return INF;
        if(l>r)
            swap(l,r);
        l++;
        int k=0;
        while((1<<(k+1))<=(r-l+1))
            k++;
        return min(ST[l][k],ST[r-(1<<k)+1][k]);
    }
    signed main(){
        int T;
        scanf("%lld",&T);
        while(T--){
            init();
            scanf("%lld",&n);
            for(int i=1;i<=n;i++){
                char c=getchar();
                while(c!='a'&&c!='b')
                    c=getchar();
                s[i]=c;
            }
            cal_sa(n);
            // printf("ok
    ");
            init_ST();
            int ans=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j+=i){
                    int midl=LCP(j,j+i);
                    int times=midl/i+1;
                    int lbe=j-i+midl%i;//j-(i-midl%i)
                    int midr=LCP(lbe,lbe+i);
                    if(midr>i)
                        times++;
                    if(times>ans)
                        ans=times;
                }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    swoole 安装方法 使用即时聊天
    git的介绍以及简单应用
    curl的应用
    linux下监听和同步代码配置
    mac skim 修改背景色
    php 编译安装的一个 configure 配置
    mac mysql error You must reset your password using ALTER USER statement before executing this statement.
    yii2 控制器里 action 大小写组合造成的路由问题
    warning : json_decode(): option JSON_BIGINT_AS_STRING not implemented in xxx
    redis 自启动脚本
  • 原文地址:https://www.cnblogs.com/dreagonm/p/10487072.html
Copyright © 2011-2022 走看看