<span>POJ - 3693 Maximum repetition substring(重复次数最多的连续重复子串)</span>

传送门: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 }

 

全部评论

相关推荐

joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
10-06 12:46
门头沟学院 Java
跨考小白:定时任务启动
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务