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\n",ch);
            continue;
        }
        for(int i=pos;i<pos+len;i++)
            printf("%c",s[i]);
        printf("\n");
    }

    return 0;
}

 

posted @ 2013-09-06 21:54  z.arbitrary  阅读(965)  评论(0编辑  收藏  举报