• POJ-3693-Maximum repetition substring(后缀数组-重复次数最多的连续重复子串)


    题意:

    给出一个串,求重复次数最多的连续重复子串

    分析:

    比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现几次。

    既然长度为L的串重复出现,那么str[0],str[l],str[2*l]……中肯定有两个连续的出现在字符串中。

    那么就枚举连续的两个,然后从这两个字符前后匹配,看最多能匹配多远。

    即以str[i*l],str[i*l+l]前后匹配,这里是通过查询suffix(i*l),suffix(i*l+l)的最长公共前缀

    通过rank值能找到i*l,与i*l+l的排名,我们要查询的是这段区间的height的最小值,通过RMQ预处理

    达到查询为0(1)的复杂度

     设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j + 1) * L - (L - M % L)的最长公共前缀。

    即把之前的区间前缀L-M%L即可。

    分析引自http://blog.csdn.net/acm_cxlove/article/details/7941205

    // File Name: 3693.cpp
    // Author: Zlbing
    // Created Time: 2013年09月06日 星期五 21时05分32秒
    
    #include<iostream>
    #include<string>
    #include<algorithm>
    #include<cstdlib>
    #include<cstdio>
    #include<set>
    #include<map>
    #include<vector>
    #include<cstring>
    #include<stack>
    #include<cmath>
    #include<queue>
    using namespace std;
    #define CL(x,v); memset(x,v,sizeof(x));
    #define INF 0x3f3f3f3f
    #define LL long long
    #define REP(i,r,n) for(int i=r;i<=n;i++)
    #define RREP(i,n,r) for(int i=n;i>=r;i--)
    //rank从0开始
    //sa从1开始,因为最后一个字符(最小的)排在第0位
    //height从2开始,因为表示的是sa[i-1]和sa[i]
    const int MAXN=220000;
    int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN];
    char s[MAXN];
    int buc[MAXN];
    void calheight(int n) {
        int i , j , k = 0;
        for(i = 1 ; i <= n ; i++) rank[sa[i]] = i;
        for(i = 0 ; i < n ; height[rank[i++]] = k)
            for(k?k--:0 , j = sa[rank[i]-1] ; s[i+k] == s[j+k] ; k++);
    }
    bool cmp(int *r,int a,int b,int l) {
        return (r[a] == r[b] && r[a+l] == r[b+l]);
    }
    void suffix(int n,int m = 128) {
        int i , l , p , *x = X , *y = Y;
        for(i = 0 ; i < m ; i ++) buc[i] = 0;
        for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i]  ] ++;
        for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
        for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
        for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
            p = 0;
            for(i = n-l ; i < n ; i ++) y[p++] = i;
            for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
            for(i = 0 ; i < m ; i ++) buc[i] = 0;
            for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
            for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
            for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
            for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
                x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
        }
        calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来
    }
    //当需要反复询问两个后缀的最长公共前缀时用到RMQ
    int Log[MAXN];
    int best[20][MAXN];
    void initRMQ(int n) {//初始化RMQ
        for(int i = 1; i <= n ; i ++) best[0][i] = height[i];
        for(int i = 1; i <= Log[n] ; i ++) {
            int limit = n - (1<<i) + 1;
            for(int j = 1; j <= limit ; j ++) {
                best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]);
            }
        }
    }
    int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
        a = rank[a];    b = rank[b];
        if(a > b) swap(a,b);
        a ++;
        int t = Log[b - a + 1];
        return min(best[t][a] , best[t][b - (1<<t) + 1]);
    }
    int main() {
        //预处理每个数字的Log值,常数优化,用于RMQ
        Log[0] = -1;
        for(int i = 1; i < MAXN ; i ++) {
            Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
        }
        int cas=0;
        //*******************************************
        //    n为数组长度,下标0开始
        //    将初始数据,保存在s里,并且保证每个数字都比0大
        //    m = max{ s[i] } + 1
        //    一般情况下大多是字符操作,所以128足够了
        //*******************************************
        while(~scanf("%s",s))
        {
            if(s[0]=='#')break;
            int n=strlen(s);
            s[n]=0;
            suffix(n+1);
            initRMQ(n);
            int maxx=0;
            int len=0;
            int pos=0;
            for(int i=1;i<=n/2;i++)
            {
                for(int j=0;j+i<n;j+=i)
                {
                    if(s[j]!=s[j+i])continue;
                   int k=lcp(j,j+i);
                   int tot=k/i+1;
                   int t=i-(k%i);
                   int p=j;
                   int cnt=0;
                   if(t&&t<i)
                   {
                       for(int m=j-1;m>j-i&&s[m]==s[m+i]&&m>=0;m--)
                       {
                           cnt++;
                           if(cnt==t)
                           {
                               tot++;
                               p=m;
                           }
                           else if(rank[p]>rank[m])
                           {
                               p=m;
                           }
                       }
                   }
                   if(tot>maxx)
                   {
                       maxx=tot;
                       pos=p;
                       len=maxx*i;
                   }
                   else if(tot==maxx)
                   {
                       if(rank[p]<rank[pos])
                       {
                           pos=p;
                           len=maxx*i;
                       }
                   }
                }
            }
            printf("Case %d: ",++cas);
            if(maxx<2)
            {
                char ch='z';
                for(int i=0;i<n;i++)
                    if(s[i]<ch)
                        ch=s[i];
                printf("%c
    ",ch);
                continue;
            }
            for(int i=pos;i<pos+len;i++)
                printf("%c",s[i]);
            printf("
    ");
        }
    
        return 0;
    }
  • 相关阅读:
    安装openssl后yum不能使用的解决办法
    使用xShell 连接 docker 使用说明
    /usr/bin/ld: cannot find -lcrypto
    Mac包管理神器:Home-brew
    FinalShell远程连接工具推荐
    make编译出错 usr/bin/ld: /data/app/openssl/lib/libcrypto.a(ecs_asn1.o): relocation R_X86_64_PC32 against symbol `ECDSA_SIG_it' can not be used when making a shared object; recompile with -fPIC
    交叉编译环境搭建
    安装Gitlab
    Git的详细使用
    服务器里Centos 7安装KVM,并通过KVM安装Centos 7
  • 原文地址:https://www.cnblogs.com/arbitrary/p/3306336.html
Copyright © 2020-2023  润新知