您的位置:首页 > 其它

POJ 3693 Maximum repetition substring (后缀数组+RMQ 求重复最多的连续子串)

2015-03-23 22:48 387 查看
给定一个字符串,求重复次数最多的连续重复子串。

先穷举长度L,然后求长度为L 的子串最多能连续出现几次。首先连续出现1 次是肯定可以的,所以这里只考虑至少2 次的情况。假设在原字符串中连续出现2 次,记这个子字符串为S,那么S 肯定包括了字符r[0], r[L], r[L*2],r[L*3], ……中的某相邻的两个。所以只须看字符r[L*i]和r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1 次。最后看最大值是多少

难点主要在于向前匹配的过程,处理的时候不是真的向前匹配,而是把LCP长度向前拓展凑成循环长度的整数倍再求LCP

//10004K	329MS	C++	3962B
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 1e5+1e3;

int t1[MAXN],t2[MAXN],c[MAXN];
bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
    n++;
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=str[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        for(i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0;i<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=1;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
        if(p>=n) break;
        m=p;
    }
    int k=0;
    n--;
    for(i=0;i<=n;i++) rank[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k) k--;
        j=sa[rank[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rank[i]]=k;
    }
}
int rank[MAXN],height[MAXN];
int RMQ[MAXN];
int mm[MAXN];
int best[20][MAXN];
void initRMQ(int n)
{
    mm[0]=-1;
    for(int i=1;i<=n;i++)
        mm[i]=((i&(i-1))==0)? mm[i-1]+1:mm[i-1];
    for(int i=1;i<=n;i++) best[0][i]=i;
    for(int i=1;i<=mm
;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
        {
            int a=best[i-1][j];
            int b=best[i-1][j+(1<<(i-1))];
            if(RMQ[a]<RMQ[b]) best[i][j]=a;
            else best[i][j]=b;
        }
}
int askRMQ(int a,int b)
{
    int t;
    t=mm[b-a+1];
    b-=(1<<t)-1;
    a=best[t][a];
    b=best[t][b];
    return RMQ[a]<RMQ[b] ? a:b;
}
int lcp(int a,int b)
{
    a=rank[a];
    b=rank[b];
    if(a>b) swap(a,b);
    return height[askRMQ(a+1,b)];
}

char str[MAXN];
int r[MAXN];
int sa[MAXN];

int cirl[MAXN];
int main()
{
    int cas=0;
    while(scanf("%s",str))
    {
        int len=strlen(str);
        if(len==1&&str[0]=='#') break;
        for(int i=0;i<len;i++) r[i]=str[i]-'a'+1;   //注意最小的字符的值必须比0大,因为da中默认0比任何字符小
        r[len]=0;
        da(r,sa,rank,height,len,30);
        for(int i=1;i<=len;i++) RMQ[i]=height[i];
        initRMQ(len);

        int maxn=-1,top=0;
        for(int l=1;l<len;l++)
            for(int p=0 ; p+l<len ; p+=l)
            {
                int t1= lcp(p,p+l);
                int tmp= t1/l+1;
                int k=p-(l-t1%l);
                if(k>=0&&t1%l)
                {
                    int t2 = lcp(k,k+l);
                    if(t2>t1) tmp++;
                }
                if(tmp>maxn)
                {
                    top=0;
                    cirl[top++]=l;
                    maxn=tmp;
                }
                else if(tmp==maxn)
                {
                    cirl[top++]=l;
                }
            }
        sort(cirl,cirl+top); //YY
        int st,ed=-1;
        for(int i=1;i<=len&&ed==-1;i++)
            for(int j=0;j<top;j++)
            {
                if(sa[i]+cirl[j]>len) continue;
                int a=sa[i],b=sa[i]+cirl[j];
                if(lcp(a,b)>=(maxn-1)*cirl[j])
                {
                    st=sa[i];
                    ed=st+cirl[j]*maxn;
                    break;
                }
            }
        str[ed]=0;
        printf("Case %d: %s\n",++cas,str+st);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐