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;
    }
    
  • 相关阅读:
    Mac下安装cscope和ctags
    MAC的VIMRC
    我的Linux系统的VIMRC
    关于在VI中查看BIN文件二进制值不对的问题
    没有关心过编写代码方式的好处,你是不是也是这样?
    四十不惑,真的不惑了么?
    jQuery通过text值来设置选定,以及遍历select的选项个数和遍历
    C#解析XML文件
    网页调用本地程序(Windows下浏览器全兼容)
    C#获取文件的md5
  • 原文地址:https://www.cnblogs.com/dreagonm/p/10487072.html
Copyright © 2011-2022 走看看