题意:
给定一个字符串 求重复次数最多的连续重复子串 并输出字典序最小方案
题解:
枚举子串长度L 显然如果重复次数>1 那么答案串肯定包含s[1],s[1+L],s[1+L*2],...中的两个
枚举被答案包含位置 1+L*i
将1+L*i和1+L*(i+1) 向前、向后匹配 记匹配距离和为k 则重复次数为k/L
这样我们就能知道最多重复次数ans是多少
但是这题还要求字典序最小方案
求方案需要再进行一次枚举 方法同上
当重复次数和答案相等时 更新方案
但是我们知道 包含1+L*i 重复次数为k/L 的方案可能有L种 这样总时间就可能达到O(n^2)
因为这L种方案的下标是连续的 并且他们的长度相等
我们可以用线段树维护下标为x到y的rank最小值位置在哪 即可log(n)完成一个询问
总复杂度O(nlog^2(n))
(但是好像常数写好一点不用线段树也能过- - 我就没加跑得比AK加了还快。。)
代码:
1 #include <cstdio> 2 #include <cstring> 3 const int N=200005; 4 int n,ans,ansx,ansy,x[N],y[N],sa[N],rank[N],hi[N],ws[N],a[N],lon,min[N][20]; 5 char s[N]; 6 void sort(int t){ 7 for (int i=1;i<=n;i++) ++ws[x[y[i]]]; 8 for (int i=1;i<=t;i++) ws[i]+=ws[i-1]; 9 for (int i=n;i;i--) sa[ws[x[y[i]]]--]=y[i]; 10 for (int i=1;i<=t;i++) ws[i]=0; 11 } 12 bool check(int a,int b,int c){ return y[a]==y[b] && y[a+c]==y[b+c]; } 13 void makesa(){ 14 int p=0,tt=200; 15 for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i; 16 sort(tt); 17 for (int j=1;j<=n;j<<=1,tt=p,p=0){ 18 for (int i=n-j+1;i<=n;i++) y[++p]=i; 19 for (int i=1;i<=n;i++) 20 if (sa[i]>j) y[++p]=sa[i]-j; 21 sort(tt); 22 for (int i=1;i<=n;i++) y[i]=x[i]; 23 x[sa[1]]=p=1; 24 for (int i=2;i<=n;i++) 25 x[sa[i]]=check(sa[i],sa[i-1],j) ? p : ++p; 26 } 27 } 28 void makehi(){ 29 for (int i=1;i<=n;i++) rank[sa[i]]=i; 30 for (int i=1,k=0;i<=n;hi[rank[i++]]=k) 31 if (rank[i]==1) k=0; 32 else for (k=k ? k-1 : k;a[i+k]==a[sa[rank[i]-1]+k];++k); 33 } 34 int minn(int x,int y){ return x<y ? x : y; } 35 void swap(int &x,int &y){ int t=x; x=y,y=t; } 36 void makemin(){ 37 for (int i=n;i;i--){ 38 min[i][0]=hi[i]; 39 for (int j=1;i+(1<<j)<=n;j++) 40 min[i][j]=minn(min[i][j-1],min[i+(1<<(j-1))][j-1]); 41 } 42 } 43 int find(int t){ 44 int l=0,r=20,mid; 45 while (l+1<r){ 46 mid=(l+r)/2; 47 if ((1<<mid)>t) r=mid; 48 else l=mid; 49 } 50 return l; 51 } 52 int getlon(int x,int y){ 53 x=rank[x],y=rank[y]; 54 if (x>y) swap(x,y); 55 int add=find(y-x); 56 //while ((1<<add)<=y-x) ++add; 57 //--add; 58 return minn(min[x+1][add],min[y-(1<<add)+1][add]); 59 } 60 bool checkres(int resx,int resy){ 61 if (!ansx) return 1; 62 int l1=resy-resx+1,l2=ansy-ansx+1,lon=getlon(resx,ansx); 63 if (lon>=l1 || lon>=l2) return l1<l2; 64 return rank[resx]<rank[ansx]; 65 } 66 void makexy(int x,int y,int z){ 67 for (int i=x;i+z-1<=y;i++){ 68 int resx=i,resy=i+z-1; 69 if (checkres(resx,resy)) ansx=resx,ansy=resy; 70 } 71 } 72 void makeans(int bo){ 73 for (int i=1;i<lon;i++) 74 for (int j=1+i;j<=lon;j+=i) 75 if (a[j]==a[j-i]){ 76 int resx=j-i-getlon(n-(j-i)+1,n-j+1)+1,resy=j+getlon(j-i,j)-1,lon=resy-resx+1,res=lon/i; 77 if (bo){ 78 if (res==ans) makexy(resx,resy,i*res); 79 }else if (ans<res) ans=res; 80 } 81 } 82 int main(){ 83 freopen("poj3693.in","r",stdin); 84 freopen("poj3693.out","w",stdout); 85 for (int t=1;scanf("%s",s),s[0]!='#';t++){ 86 printf("Case %d: ",t); 87 lon=strlen(s); 88 n=0; 89 for (int i=0;i<lon;i++) a[++n]=s[i]; 90 a[++n]='~'; 91 for (int i=lon-1;i>=0;i--) a[++n]=s[i]; 92 a[n+1]=0; 93 ans=1; 94 ansx=ansy=0; 95 makesa(); 96 makehi(); 97 makemin(); 98 makeans(0); 99 if (ans==1) printf("%c ",s[sa[1]-1]); 100 else{ 101 if (t==6) 102 t=6; 103 makeans(1); 104 for (int i=ansx;i<=ansy;i++) printf("%c",s[i-1]); 105 puts(""); 106 } 107 } 108 fclose(stdin); 109 fclose(stdout); 110 }