POJ 3693 Maximum repetition substring(后缀数组+ST)

来源:互联网 发布:下载ps软件 编辑:程序博客网 时间:2024/04/30 22:28

Description
给出一个字符串,求这个字符串的一个子串,使得其完全由最小循环节组成且循环次数最多,如果有多组解则输出字典序最小的
Input
多组用例,每组用例占一行为一行长度不超过100000的字符串,以#结束输入
Output
对于每组用例,输出满足条件的字典序最小的子串
Sample Input
ccabababc
daabbccaa
#
Sample Output
Case 1: ababab
Case 2: aa
Solution
如果某子串是由几个最小循环节构成,设最小循环节长度为l,那么s[0],s[l],s[2l],…,s[[len/l]*l]中至少由两个出现在这个子串中,那么从1到n枚举l,对于每个i,求出k=lcp(i,i+l)(用ST表预处理height数组可以O(1)查询区间最小值求lcp(a,b)),那么循环次数最少为cnt=k/l+1次,但这样是假设i和i+l是两个循环节的起始,但很多情况下不是,所以还需要往i前面移动一点寻求更长的匹配,移动的长度就是l-k%l,因为剩余的k%l已经有匹配了,如果能把剩下的l-k%l匹配上,那么循环节次数就多了一个,所以下次求kk=lcp(t,t+l),其中t=i-(l-k%l),如果kk>=k则cnt++,之后拿cnt更新最优解即可,注意要将循环节次数相等的l值都记录下来放到ans数组中,因为之后需要从中找字典序最小的,找最小字典序的解就是按sa数组往下寻找,对于每个sa[i],如果lcp(sa[i],sa[i]+ans[j])>=(Max-1)*ans[j],则说明从sa[i]开始,长度为Max*ans[j]的子串就是符合条件的最优解
Code

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;#define maxn 111111int t1[maxn],t2[maxn],c[maxn],sa[maxn],rank[maxn],height[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 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 RMQ[maxn],mm[maxn],best[22][maxn];void Init_rmq(int n){    for(int i=1;i<=n;i++)RMQ[i]=height[i];    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 Ask_rmq(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[Ask_rmq(a+1,b)];}int n,a[maxn],ans[maxn],res;char s[maxn];int main(){    int Case=1;    while(~scanf("%s",s),s[0]!='#')    {        n=strlen(s);        for(int i=0;i<n;i++)a[i]=s[i];        a[n]=0;        da(a,n,333);        Init_rmq(n);        int Max=-1;        for(int l=1;l<n;l++)        {            for(int i=0;i+l<n;i+=l)            {                int k=lcp(i,i+l);                int cnt=k/l+1,t=l-k%l;                t=i-t;                if(t>=0&&k%l)                    if(lcp(t,t+l)>=k)cnt++;                if(cnt>Max)                {                    Max=cnt;                    res=0;                    ans[res++]=l;                }                else if(cnt==Max)ans[res++]=l;            }        }        int pos,len,flag=0;        for(int i=1;i<=n;i++)        {            if(flag)break;            for(int j=0;j<res;j++)            {                int temp=ans[j];                if(lcp(sa[i],sa[i]+temp)>=(Max-1)*temp)                {                    pos=sa[i];                    len=Max*temp;                    flag=1;                    break;                }            }        }        printf("Case %d: ",Case++);        for(int i=0;i<len;i++)printf("%c",s[i+pos]);        printf("\n");    }    return 0;}
0 0
原创粉丝点击