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 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这道题在后缀数组的论文里有详细的解释,还有图来供大家理解,可以看看。
/*
 * POJ 3693 Maximum repetition substring
 * 寻找重复次数最多的连续子串 *
 * 后缀数组的论文里面有很详细的解释
 * 首先枚举重复出现的子串的长度,然后求它最多能出现多少次。
 * 如何求呢,首先长度为l的子串,这个字符串肯定至少出现2次,那么连续的子串肯定包含str[0],str[l],str[2*l]
 * ....str[k*l],中连续的两个,这样我们同样枚举起始的i,i+l进行前后匹配
 * 向后匹配:即求后缀i和i+l的最长公共前缀L,这个可以用RMQ+height数组在O(n*logn)内预处理,O(1)的查询
 * 向前匹配:如果上一步得到的L能被l整除,则无需向前匹配,因为前面肯定不可能再匹配到。如果L%l!=0,那么可能
 * 起始的位置是在i前面一点,这样我们可能会有更优的解,向前多少呢,因为后面已经有L-L%l的长度是合适的了,如
 * 果还有,那只可能往前移动(l-L%l),这样才会增加一个重复的次数,于是我们只需要找到lcp(i-(l-L%l),i-(l-L%l)+l)
 * 如果这个大于L,说明还存在更多的次数
 * 这样就找到了重复子串的长度,由于题目要求输出字典序最小的子串,我们可以遍历sa数组,找到的第一个符合条件的
 * 字符串肯定就是我们需要的答案,因为sa数组就是按字典序排序的。 *
 * 枚举长度的时间是O(n),每次计算的时间是O(n/l)的,这样总的时间复杂度是
 * O(n/1+n/2+n/3+....+n/n)=O(n*logn)
 */

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;

const int MAXN = 100000+100;

int sa[MAXN];
int t1[MAXN],t2[MAXN],c[MAXN];
int Rank[MAXN],height[MAXN];
void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[i]]++;
    for(i=1;i<m;i++)c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        for(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<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}
void getHeight(int s[],int n)
{
    int i,j,k=0;
    for(i=0;i<=n;i++) Rank[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)k--;
        j=sa[Rank[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[Rank[i]]=k;
    }
}
struct SparseTable
{
    int rmq[MAXN];
    int mm[MAXN];
    int dp[MAXN][20];
    void init(int n)
    {
        mm[0]=-1;
        for(int i=1;i<=n;i++)
        {
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            dp[i][0]=i;
        }
        for(int j=1;j<=mm[n];j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                dp[i][j]=rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)
    {
        if(a>b) swap(a,b);
        int k=mm[b-a+1];
        return rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
}ST;
int lcp(int a,int b)
{
    a=Rank[a],b=Rank[b];
    if(a>b) swap(a,b);
    return height[ST.query(a+1,b)];
}
char str[MAXN];
int ss[MAXN];
int a[MAXN];

int main()
{
    int iCase=0;
    while(scanf("%s",str)==1)
    {
        if(str[0]==‘#‘) break;
        iCase++;
        int n=strlen(str);
        for(int i=0;i<=n;i++) ss[i]=str[i];
        build_sa(ss,n+1,128);
        getHeight(ss,n);
        for(int i=1;i<=n;i++) ST.rmq[i]=height[i];
        ST.init(n);
        int cnt=0,ma=0;
        for(int l=1;l<=n;l++)//枚举重复子串的长度
        {
            for(int i=0;i+l<n;i+=l)
            {
                int ll=lcp(i,i+l);
                int num=ll/l+1;
                int pre=i-(l-ll%l);//往前找
                if(pre>=0&&ll%l)
                {
                    if(lcp(pre,pre+l)>=ll) num++;
                }
                if(num>ma)
                {
                    ma=num;
                    cnt=0;
                    a[cnt++]=l;//不同长度可能出现了同样的次数
                }
                else if(num==ma) a[cnt++]=l;
            }
        }
        int len=-1,st;
        for(int i=1;i<=n&&len==-1;i++)
        {
            for(int j=0;j<cnt;j++)
            {
                int l=a[j];
                if(lcp(sa[i],sa[i]+l)>=(ma-1)*l)//在sa数组找到的第一个就是字典序最小的
                {
                    len=l;
                    st=sa[i];
                    break;
                }
            }
        }
        str[st+len*ma]=0;
        printf("Case %d: %s\n",iCase,str+st);
    }
    return 0;
}

 
时间: 2024-10-13 22:25:34

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

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(后缀数组)

题目链接: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(后缀数组+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&quo

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(后缀数组+ST表)

[题目链接] poj.org/problem?id=3693 [题目大意] 求一个串重复次数最多的连续重复子串并输出,要求字典序最小. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+l]前缀匹配得到的最长长度为r, 枚举所有的l和i,得到r,那么答案就是r/l+1的最大值. 计算任意后缀的最长公共前缀可以利用后缀数组+ST表来解决, 两个后缀的最长公共前缀就是他们名次之间的h数组的最小值. 显然,枚举i和l的复杂度达到了O(n2),是没有办法完成统计的, 我们发现每个区段只会存

poj 3693 Maximum repetition substring(08合肥 RMQ+后缀数组)

传送门:http://poj.org/problem?id=3693 题目:给出一个串,求重复次数最多的连续重复子串: 分析: 枚举重复单元的长度,然后理所当然的枚举起点.利用rmq处理,后缀i,i+l的最长前缀. lcp/l+1,为当前相邻l长度单元的串的重复次数,但是由于i+=l,提高了效率,但是i不一定刚好是重复串的起点,所以如果r%l!=0,把串往前移l-r%l个单位.找到最大的重复次数,然后利用sa数组的排名(1-n),,可以字典序输出. 详细链接:http://blog.csdn.n

后缀数组 POJ 3693 Maximum repetition substring

题目链接 题意:给定一个字符串,求重复次数最多的连续重复子串. 分析:(论文上的分析)先穷举长度 L,然后求长度为 L 的子串最多能连续出现几次.首先连续出现 1 次是肯定可以的,所以这里只考虑至少 2 次的情况.假设在原字符串中连续出 现 2 次,记这个子字符串为 S,那么 S 肯定包括了字符 r[0], r[L], r[L*2], r[L*3], ……中的某相邻的两个.所以只须看字符 r[L*i]和 r[L*(i+1)]往前和 往后各能匹配到多远,记这个总长度为 K,那么这里连续出现了 K/

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

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