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 d[maxn][50];
 67 void rmq_init(int n) {
 68     //d[i][j]表示从第i位开始连续2^j个数中的最小值。
 69     for(int i = 0; i < n; i++) d[i][0] = height[i];
 70     for(int j = 1; (1<<j) <= n; j++)
 71         for(int i = 0; i + (1<<j) - 1 < n; i++)
 72             d[i][j] = min(d[i][j-1], d[i+(1<<(j-1))][j-1]);        //从第i为开始的2^j个数的前半段(第i位开始的2^(j-1)个数)和后半段(第i+2^(j-1)开始的2^(j-1)个数)中取最小
 73 }
 74 int rmq(int l, int r,int n) {
 75     if(l == r) return n-l;
 76     if(rnk[l] > rnk[r]) swap(l, r);
 77     int L = rnk[l]+1;
 78     int R = rnk[r];
 79     int k = 0;
 80     while((1<<(k+1)) <= R-L+1) k++;
 81     return min(d[L][k], d[R-(1<<k)+1][k]);
 82 }
 83 
 84 int main()
 85 {
 86     ios::sync_with_stdio(false);
 87     cin.tie(0);
 88     cout.tie(0);
 89     int t=1;
 90     while(cin>>s){
 91         if(s[0]=='#') break;
 92         int len=strlen(s);
 93         for(int i=0;i<len;i++) r[i]=s[i]-'a'+1;
 94         r[len]=0;
 95         getsa(r,sa,len,150);
 96         getheight(r,len);
 97         rmq_init(len);
 98         int ans=0;
 99         int pos=0,p=0;
100         for(int k=1;k<len;k++){     //枚举长度
101             for(int i=0;i+k<len;i+=k){
102                 int n=rmq(i,i+k,len);
103                 n--;
104                 for(int j=0;j<=k-1;j++){
105                     int now=i-j;
106                     if((now<0||s[now]!=s[now+k])&&j) break;
107                     n++;
108                     int sum=n/k+1;
109                     if(sum>ans||(sum==ans&&rnk[now]<rnk[pos])){
110                         ans=sum;
111                         pos=now;
112                         p=k;
113                     }
114                 }
115             }
116         }
117         cout<<"Case "<<t++<<": ";
118         if(ans<=1){
119             char tmp='z';
120             for(int i=0;i<len;i++) tmp=min(tmp,s[i]);
121             cout<<tmp<<endl;
122         }
123         else{
124             for(int i=0;i<ans*p;i++){
125                 cout<<s[i+pos];
126             }
127             cout<<endl;
128         }
129     }
130     return 0;
131 }

猜你喜欢

转载自www.cnblogs.com/lilibuxiangtle/p/12635224.html