poj3693-重复次数最多的连续重复子串后缀数组
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj3693-重复次数最多的连续重复子串后缀数组相关的知识,希望对你有一定的参考价值。
题意:给定一个串,长度<=10^5,求它重复次数最多的连续重复子串(输出字典序最小的那个)。
例如ccabcabc,答案就是abcabc
一开始没想清楚,结果调了好久。
原理:
按照L划分,因为相邻两个i之间隔着一个L,s[i*L]和s[(i+1)*L]必定是真正循环节的同一个位置。
对于当前的L,i,i+1,x=s[i*L],y=s[(i+1)*L],找前找后,知道了最早能匹配到t0,最晚能匹配到t1,因为不知道当前的起始点是真正循环节的第几个点,所以我们要往前找L个点看看它们是不是真正的起始点。
细节就看代码吧:
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> using namespace std; const int N=2*100100; int cl,rk[N],sa[N],Rs[N],y[N],wr[N],h[N],r[N][30]; char c[N]; int minn(int x,int y){return x<y ? x:y;} void get_sa(int m) { for(int i=1;i<=cl;i++) rk[i]=c[i]-\'a\'+1; for(int i=1;i<=m;i++) Rs[i]=0; for(int i=1;i<=cl;i++) Rs[rk[i]]++; for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i; int ln=1,p=0; while(p<cl) { int k=0; for(int i=cl-ln+1;i<=cl;i++) y[++k]=i; for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln; for(int i=1;i<=cl;i++) wr[i]=rk[y[i]]; for(int i=1;i<=m;i++) Rs[i]=0; for(int i=1;i<=cl;i++) Rs[wr[i]]++; for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i]; for(int i=1;i<=cl;i++) wr[i]=rk[i]; for(int i=cl+1;i<=cl+ln;i++) wr[i]=0; p=1;rk[sa[1]]=1; for(int i=2;i<=cl;i++) { if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++; rk[sa[i]]=p; } ln*=2,m=p; } sa[0]=0,rk[0]=0; } void get_h() { int k=0,j; for(int i=1;i<=cl;i++) if(rk[i]!=1) { j=sa[rk[i]-1]; if(k) k--; while(c[j+k]==c[i+k] && j+k<=cl && i+k<=cl) k++; h[rk[i]]=k; } h[1]=0; } void get_rmq() { for(int i=1;i<=cl;i++) r[i][0]=h[i]; for(int j=1;(1<<j)<=cl;j++) for(int i=1;i+(1<<j)-1<=cl;i++) { r[i][j]=minn(r[i][j-1],r[i+(1<<(j-1))][j-1]); } } int query_rmq(int i,int j) { if(i>j) swap(i,j); i++; int k=0; while(i+(1<<(k+1)) <= j) k++; return minn(r[i][k],r[j-(1<<k)+1][k]); } int main() { int x,y,z,t0,t1,now,ans,al,ar,T=0; while(1) { scanf("%s",c+1); cl=strlen(c+1); if(cl==1 && c[1]==\'#\') return 0; printf("Case %d: ",++T); get_sa(30); get_h(); get_rmq(); ans=0;al=ar=0; for(int L=1;L*2<=cl;L++) { for(int i=0;L*(i+1)+1<=cl;i++) { x=L*i+1,y=L*(i+1)+1; if(c[x]!=c[y]) continue; z=query_rmq(rk[x],rk[y]); t1=y+z-1; t0=0; for(int j=0;j<=L-1;j++)//往前匹配 { if(x-j<1 || c[x-j]!=c[y-j]) break; t0=x-j; now=((t1-t0+1)/L); if(now>ans || (now==ans && rk[t0]<rk[al])) ans=now,al=t0,ar=t0+now*L-1; } } } if(ans==0) printf("%c\\n",c[sa[1]]); else { for(int i=al;i<=ar;i++) printf("%c",c[i]);printf("\\n"); } } return 0; }
以上是关于poj3693-重复次数最多的连续重复子串后缀数组的主要内容,如果未能解决你的问题,请参考以下文章
POJ 3693 Maximum repetition substring(后缀数组[重复次数最多的连续重复子串])
POJ 3693 Maximum repetition substring(后缀数组[重复次数最多的连续重复子串])
poj 3693 Maximum repetition substring 重复次数最多的连续子串