POJ - 3693 Maximum repetition substring(重复次数最多的连续重复子串)
传送门:POJ - 3693
题意:给你一个字符串,求重复次数最多的连续重复子串,如果有一样的,取字典序小的字符串。
题解:
比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现几次。既然长度为L的串重复出现,那么str[0],str[l],str[2*l]……中肯定有两个连续的出现在字符串中。
那么就枚举连续的两个,然后从这两个字符前后匹配,看最多能匹配多远。即以str[i*l],str[i*l+l]前后匹配,这里是通过查询suffix(i*l),suffix(i*l+l)的最长公共前缀。通过rank值能找到i*l,与i*l+l的排名,我们要查询的是这段区间的height的最小值,通过RMQ预处理达到查询为0(1)的复杂度
设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j+ 1) * L - (L - M % L)的最长公共前缀。即把之前的区间前缀L-M%L即可。
然后把可能取到最大值的长度L保存,由于 题目要求字典序最小,通过sa数组进行枚举,取到的第一组,肯定是字典序最小的。
题解的出处:https://blog.csdn.net/acm_cxlove/article/details/7941205
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 #include<iostream> 5 #include<cmath> 6 #include<cstring> 7 using namespace std; 8 9 const int maxn = 1e5+10; 10 int wa[maxn],wb[maxn],wsf[maxn],wv[maxn],sa[maxn]; 11 int rnk[maxn],height[maxn]; 12 char s[maxn]; 13 int r[maxn]; 14 15 //sa:字典序中排第i位的起始位置在str中第sa[i] sa[1~n]为有效值 16 17 //rnk:就是str第i个位置的后缀是在字典序排第几 rnk[0~n-1]为有效值 18 19 //height:字典序排i和i-1的后缀的最长公共前缀 height[2~n]为有效值,第二个到最后一个 20 21 int cmp(int *r,int a,int b,int k) 22 { 23 return r[a]==r[b]&&r[a+k]==r[b+k]; 24 } 25 26 void getsa(int *r,int *sa,int n,int m)//n为添加0后的总长 27 { 28 int i,j,p,*x=wa,*y=wb,*t; 29 for(i=0; i<m; i++) wsf[i]=0; 30 for(i=0; i<=n; i++) wsf[x[i]=r[i]]++; 31 for(i=1; i<m; i++) wsf[i]+=wsf[i-1]; 32 for(i=n; i>=0; i--) sa[--wsf[x[i]]]=i; 33 p=1; 34 j=1; 35 for(; p<=n; j*=2,m=p){ 36 for(p=0,i=n+1-j; i<=n; i++) y[p++]=i; 37 for(i=0; i<=n; i++) if(sa[i]>=j) y[p++]=sa[i]-j; 38 for(i=0; i<=n; i++) wv[i]=x[y[i]]; 39 for(i=0; i<m; i++) wsf[i]=0; 40 for(i=0; i<=n; i++) wsf[wv[i]]++; 41 for(i=1; i<m; i++) wsf[i]+=wsf[i-1]; 42 for(i=n; i>=0; i--) sa[--wsf[wv[i]]]=y[i]; 43 swap(x,y); 44 x[sa[0]]=0; 45 for(p=1,i=1; i<=n; i++) 46 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++; 47 } 48 } 49 50 void getheight(int *r,int n)//n为添加0后的总长 51 { 52 int i,j,k=0; 53 for(i=1; i<=n; i++) rnk[sa[i]]=i; 54 for(i=0; i<n; i++){ 55 if(k) 56 k--; 57 else 58 k=0; 59 j=sa[rnk[i]-1]; 60 while(r[i+k]==r[j+k]) 61 k++; 62 height[rnk[i]]=k; 63 } 64 } 65 66 int dp[maxn][40]; 67 68 void rmq_init(int n){ 69 70 int m=floor(log(n+0.0)/log(2.0)); 71 for(int i=1;i<=n;i++)dp[i][0]=height[i]; 72 for(int j=1;j<=m;j++){ 73 for(int i=n;i;i--){ 74 dp[i][j]=dp[i][j-1]; 75 if(i+(1<<(j-1))<=n){ 76 dp[i][j]=min(dp[i][j],dp[i+(1<<(j-1))][j-1]); 77 } 78 } 79 } 80 } 81 82 int rmq(int l,int r){ 83 84 int a=rnk[l],b=rnk[r]; 85 if(a>b)swap(a,b); 86 a++; 87 int k=floor(log(b-a+1.0)/log(2.0)); 88 return min(dp[a][k],dp[b-(1<<k)+1][k]); 89 } 90 91 int main() 92 { 93 ios::sync_with_stdio(false); 94 cin.tie(0); 95 cout.tie(0); 96 int t=1; 97 while(cin>>s){ 98 if(s[0]=='#') break; 99 int len=strlen(s); 100 for(int i=0;i<len;i++) r[i]=s[i]-'a'+1; 101 r[len]=0; 102 getsa(r,sa,len,150); 103 getheight(r,len); 104 rmq_init(len); 105 int ans=0; 106 int pos=0,p=0; 107 for(int k=1;k<len;k++){ //枚举长度 108 for(int i=0;i+k<len;i+=k){ //第i段 109 int n=rmq(i,i+k); //每一段的公共前缀最小值 110 n--; 111 for(int j=0;j<=k-1;j++){ //枚举每一段的起点 112 int now=i-j; 113 if((now<0||s[now]!=s[now+k])&&j) break; 114 n++; 115 int sum=n/k+1; 116 if(sum>ans||(sum==ans&&rnk[now]<rnk[pos])){ 117 ans=sum; 118 pos=now; 119 p=k; 120 } 121 } 122 } 123 } 124 cout<<"Case "<<t++<<": "; 125 if(ans<=1){ 126 char tmp='z'; 127 for(int i=0;i<len;i++) tmp=min(tmp,s[i]); 128 cout<<tmp<<endl; 129 } 130 else{ 131 for(int i=0;i<ans*p;i++){ 132 cout<<s[i+pos]; 133 } 134 cout<<endl; 135 } 136 } 137 return 0; 138 }