POJ 3693 Maximum repetition substring(后缀数组+RMQ)

Maximum repetition substring

The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1.

Given a string containing lowercase letters, you are to find a substring of it with maximum repetition number.

Input

The input consists of multiple test cases. Each test case contains exactly one line, which
gives a non-empty string consisting of lowercase letters. The length of the string will not be greater than 100,000.

The last test case is followed by a line containing a ‘#‘.

Output

For each test case, print a line containing the test case
number( beginning with 1) followed by the substring of maximum
repetition number. If there are multiple substrings of maximum
repetition number, print the lexicographically smallest one.

Sample Input

ccabababc
daabbccaa
#

Sample Output

Case 1: ababab
Case 2: aa

分析:题意为求重复次数最多的连续子串。  可以枚举长度L,找长度为L的字符串最多连续出现的次数 如果有长度为L的字符串重复出现,那么str[0],str[L],str[2*L]...这种字符,肯定会有两个连续的,在重复出现的连续字符串上.  那么找str[i*L],str[(i+1)*L]的最大公共前缀,就可以找到它们向后匹配的重复连续字符串的长度h。  这个时候还需要找 它是否能够向前匹配到字符。  设k=L-h%L; 那么它需要向前匹配一轮需要的字符为k个  这样就可以转化为判断str[i*L-k]和str[(i+1)*L-k]的最大公共前缀能否到达h  匹配的字符数/L+1为当前长度为L的字符串出现次数  找最大公共前缀的时候,需要用RMQ进行height的预处理操作,  找到最大的出现次数后,用数组记录能达到最大出现次数的长度L(方便输出字典序优先的字符串)  最后需要输出字典序最小的字符串,那么因为后缀排名越靠前,字典序较小.  所以按后缀排名进行遍历,找到字典序最小的字符串.代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
typedef long long ll;
using namespace std;
const int MAXN=100010;
int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN];
char str[MAXN];
int st[MAXN];
int minsum[MAXN][20];
void RMQ_In(int num) //预处理->O(nlogn)
{
    for(int j = 1; j < 20; ++j)
        for(int i = 1; i <= num; ++i)
            if(i + (1 << j) - 1 <= num)
            {
                minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
            }
}
int  RMQ_Query(int src,int des)
{
        int minn;
        int k=(int)(log(des-src+1.0)/log(2.0));
        minn=min(minsum[src][k],minsum[des-(1<<k)+1][k]);
        return minn;
}

int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(const char r[],int sa[],int n,int m)  //n为len+1,m一般比数组中最大的数大一点即可
{
      int i,j,p,*x=wa,*y=wb,*t;
      for(i=0; i<m; i++) Ws[i]=0;
      for(i=0; i<n; i++) Ws[x[i]=r[i]]++;
      for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
      for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
      for(j=1,p=1; p<n; j*=2,m=p)
      {
            for(p=0,i=n-j; i<n; i++) y[p++]=i;
            for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0; i<n; i++) wv[i]=x[y[i]];
            for(i=0; i<m; i++) Ws[i]=0;
            for(i=0; i<n; i++) Ws[wv[i]]++;
            for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
            for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
                  x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
      }
      return;
}
int sa[MAXN],Rank[MAXN],height[MAXN];// sa是通过后缀排名找到它在字符串中的位置,rank是根据位置找到后缀排名,两者相逆,该模板中sa数组的最小值为1。

void calheight(const char *r,int *sa,int n)
{
      int i,j,k=0;
      for(i=1; i<=n; i++) Rank[sa[i]]=i;
      for(i=0; i<n; height[Rank[i++]]=k)
            for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
}
int ct[MAXN];
int main()
{
  int t,len,maxx,r,a,b,c,h,q1,q2,tem,h2,ans,y,times,cnt=0,anslen,s,start,k2,Case=0;
   while(scanf("%s",str)!=EOF){
        Case++;
        maxx=0;
      if(str[0]==‘#‘)break;
      len=strlen(str);
      da(str,sa,len+1,130);
      calheight(str,sa,len);
      for(int i=2;i<len;i++)
          minsum[i][0]=height[i];
          RMQ_In(len);
      for(int k=1;k<len;k++)
      {
          for(int j=0;j<len;j+=k)
          {
              if(j+k>=len)
                break;
           a=min(Rank[j],Rank[j+k]);
           b=max(Rank[j],Rank[j+k]);
           h=RMQ_Query(a+1,b);
           times=h/k+1;
           y=h%k;
            if(j-(k-h%k)>=0)
            {
              q1=j-(k-h%k);
              q2=j+k-(k-h%k);
              int a=min(Rank[q1],Rank[q2]);
              int b=max(Rank[q1],Rank[q2]);
              h2=RMQ_Query(a+1,b);
              if(h2>=h)
              times++;
            }
             if(times>maxx){
                maxx=times;
                cnt=0;
             }
             if(times==maxx)
                st[cnt++]=k;

             }
           // maxx=max(ans,maxx);
        }
          anslen=-1;
          for(int i=1;i<=len&&anslen==-1;i++)
            for(int j=0;j<cnt;j++)
            {
                int a=min(i,Rank[sa[i]+st[j]]);
                int b=max(i,Rank[sa[i]+st[j]]);
               s=RMQ_Query(a+1,b);
               if(s>=(maxx-1)*st[j])
                {
                 start=sa[i];
                // cout<<"start="<<start<<endl;
                 anslen=maxx*st[j];
                 break;
               }
            }
     printf("Case %d: ",Case);
      for(int i=start;i<start+anslen;i++)
       printf("%c",str[i]);

        printf("\n");
      }

return 0;
}
				
时间: 2024-10-13 11:31:05

POJ 3693 Maximum repetition substring(后缀数组+RMQ)的相关文章

POJ - 3693 Maximum repetition substring 后缀数组+RMQ

http://poj.org/problem?id=3693 整体思路就是枚举长度L,看长度为L的字符串在s中连续出现了几次. 既然长度为L的串是重复出现的,那么s[0]-s[L]-s[2*L]-中相邻的两个一定出现在重复的L串中.(并不一定在首尾位置,也可能出现在中间). 那么我们求i*L和(i+1)*L的LCP,假设答案为M,那么答案应该是M/L+1(加一是因为i*L~(i+1)*L也是答案). 但是这个答案不一定是最优,举一个下面的例子. 此时M/L+1是3,而正确答案是4.为什么呢?是因

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>

POJ 3693 Maximum repetition substring(后缀数组求最长重复子串)

题目大意:和spoj687类似,就是当长度相同是需要输出一个最小的字典序的序列. 解体思路:这次需要枚举所有的从i到d = i-L/i (d = i-L%i)的位置,然后记录保证最大值的同时,求出来字典序最小的. Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7418   Accepted: 2217 Description The repetition numb

POJ - 3693 Maximum repetition substring(后缀数组求重复次数最多的连续重复子串)

Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1. Given a

POJ 3693 Maximum repetition substring ——后缀数组

重复次数最多的字串,我们可以枚举循环节的长度. 然后正反两次LCP,然后发现如果长度%L有剩余的情况时,答案是在一个区间内的. 所以需要找到区间内最小的rk值. 两个后缀数组,四个ST表,$\Theta(n\log n)$ 就可以解决了 空间卡死了,瞎晶胞卡过去了. #include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #incl

POJ3693:Maximum repetition substring(后缀数组+RMQ)

Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1. Given a

POJ 3693 Maximum repetition substring (后缀数组)

题目大意: 求出字典序最小,重复次数最多,的子串. 思路分析: RMQ + height 数组可以求出任意两个后缀的lcp 我们枚举答案字符串的重复的长度. 如果这个字符串的长度为 l ,而且这个字符串出现过两次或两次以上 那么你会发现在原串中  str[0] str[l] str[2*l] ....肯定有相邻的两个被包含在重复的串中. 我们求出这两个相邻的后缀的lcp 我们上面仅仅说的是被包含在重复的串中,但并不一定就是以 str[0], str[l],str[2*l]....为起点的. 那我

POJ 3693 Maximum repetition substring(后缀数组神题)

POJ 3693 Maximum repetition substring 题目链接 题意:给定一个字符串,求出其子串中,重复次数最多的串,如果有相同的,输出字典序最小的 思路:枚举长度l,把字符串按l分段,这样对于长度为l的字符串,肯定会包含一个分段位置,这样一来就可以在每个分段位置,往后做一次lcp,求出最大匹配长度,然后如果匹配长度有剩余,看剩余多少,就往前多少位置再做一次lcp,如果匹配出来长度更长,匹配次数就加1,这样就可以枚举过程中保存下答案了 这样问题还有字典序的问题,这个完全可以

POJ 3693 Maximum repetition substring (寻找重复次数最多的连续子串)

Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9083   Accepted: 2782 Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same conse

poj3693 Maximum repetition substring 后缀数组

http://poj.org/problem?id=3693 Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7241   Accepted: 2162 Description The repetition number of a string is defined as the maximum number R such that the string can b