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

来源:互联网 发布:阿里巴巴与淘宝价格比 编辑:程序博客网 时间:2024/04/29 18:09



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


先穷举长度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



//10004K329MSC++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[n];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;}


0 0