SPOJREPEATS-Repeats

REPEATS - Repeats

no tags 

A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed string t with length l>=1. For example, the string

s = abaabaabaaba

is a (4,3)-repeat with t = aba as its seed string. That is, the seed string t is 3 characters long, and the whole string s is obtained by repeating t 4 times.

Write a program for the following task: Your program is given a long string u consisting of characters ‘a’ and/or ‘b’ as input. Your program must find some (k,l)-repeat that occurs as substring within u with k as large as possible. For example, the input string

u = babbabaabaabaabab

contains the underlined (4,3)-repeat s starting at position 5. Since u contains no other contiguous substring with more than 4 repeats, your program must output the maximum k.

Input

In the first line of the input contains H- the number of test cases (H <= 20). H test cases follow. First line of each test cases is n - length of the input string (n <= 50000), The next n lines contain the input string, one character (either ‘a’ or ‘b’) per line, in order.

Output

For each test cases, you should write exactly one interger k in a line - the repeat count that is maximized.

Example

Input:
1
17
b
a
b
b
a
b
a
a
b
a
a
b
a
a
b
a
b

Output:
4
since a (4, 3)-repeat is found starting at the 5th character of the input string.


题意:给定一个字符串,求重复次数最多的连续重复子串的重复次数

解题思路:首先连续出现1次是肯定可以的,所以这里只考虑至少2次的情况。当枚举到合适的子串长度时,在枚举r[L*i]和r[L*(i+1)]的过程中,必然可以出现r[L*i]在第一个循环节里,而r[L*(i+1)]在第二个循环节里的这种情况,如果此时r[L*i]是第一个循环节的首字符,这样直接用公共前缀k除以i并向下取整就可以得到最后结果。但如果r[L*i]如果不是首字符,这样算完之后结果就有可能偏小,因为r[L*i]前面可能还有少许字符也能看作是第一个循环节里的。先算出从r[L*i]开始,除匹配了k/i个循环节,还剩余了几个字符,剩余的自然是k%i个字符。如果说r[L*i]的前面还有i-k%i个字符完成比配的话,这样就相当于还可以再匹配出一个循环节,所以只要检查一下从r[L*i-(i-k%i)]和r[L*i-(i-k%i)+i]开始是否有i-k%i个字符能够完成匹配即可,也就是检查这两个后缀的最长公共前缀是否比i-k%i大即可



#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <map>

using namespace std;

const int N=200010;
const int INF=0x7FFFFFFF;

struct Sa
{
    char s[N];
    int rk[2][N],sa[N],h[N],w[N],now,n;
    int rmq[N][20],lg[N],bel[N];

    bool GetS()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%s",s+i);
        return true;
    }

    void getsa(int z,int &m)
    {
        int x=now,y=now^=1;
        for(int i=1; i<=z; i++) rk[y][i]=n-i+1;
        for(int i=1,j=z; i<=n; i++)
            if(sa[i]>z) rk[y][++j]=sa[i]-z;
        for(int i=1; i<=m; i++) w[i]=0;
        for(int i=1; i<=n; i++) w[rk[x][rk[y][i]]]++;
        for(int i=1; i<=m; i++) w[i]+=w[i-1];
        for(int i=n; i>=1; i--) sa[w[rk[x][rk[y][i]]]--]=rk[y][i];
        for(int i=m=1; i<=n; i++)
        {
            int *a=rk[x]+sa[i],*b=rk[x]+sa[i-1];
            rk[y][sa[i]]=*a==*b&&*(a+z)==*(b+z)?m-1:m++;
        }
    }

    void getsa(int m)
    {
        now=rk[1][0]=sa[0]=s[0]=0;
        n=strlen(s+1);
        for(int i=1; i<=m; i++) w[i]=0;
        for(int i=1; i<=n; i++) w[s[i]]++;
        for(int i=1; i<=m; i++) rk[1][i]=rk[1][i-1]+(bool)w[i];
        for(int i=1; i<=m; i++) w[i]+=w[i-1];
        for(int i=1; i<=n; i++) rk[0][i]=rk[1][s[i]];
        for(int i=1; i<=n; i++) sa[w[s[i]]--]=i;
        rk[1][n+1]=rk[0][n+1]=0;
        for(int x=1,y=rk[1][m]; x<=n&&y<=n; x<<=1) getsa(x,y);
        for(int i=1,j=0; i<=n; h[rk[now][i++]]=j?j--:j)
        {
            if(rk[now][i]==1) continue;
            int k=n-max(sa[rk[now][i]-1],i);
            while(j<=k&&s[sa[rk[now][i]-1]+j]==s[i+j]) ++j;
        }
    }

    void getrmq()
    {
        h[n+1]=h[1]=lg[1]=0;
        for(int i=2; i<=n; i++)
            rmq[i][0]=h[i],lg[i]=lg[i>>1]+1;
        for(int i=1; (1<<i)<=n; i++)
        {
            for(int j=2; j<=n; j++)
            {
                if(j+(1<<i)>n+1) break;
                rmq[j][i]=min(rmq[j][i-1],rmq[j+(1<<i-1)][i-1]);
            }
        }
    }

    int lcp(int x,int y)
    {
        int l=min(rk[now][x],rk[now][y])+1,r=max(rk[now][x],rk[now][y]);
        return min(rmq[l][lg[r-l+1]],rmq[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
    }

    void work()
    {
        getsa(300);
        getrmq();
        int ans=1;
        for(int L=1;L<=n;L++)
        {
            for(int i=1;i+L<=n;i+=L)
            {
                int R=lcp(i,i+L);
                ans=max(ans,R/L+1);
                if(i>L-R%L)
                    ans=max(lcp(i-L+R%L,i+R%L)/L+1,ans);
            }
        }
        printf("%d\n",ans);
    }
}sa;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        sa.GetS();
        sa.work();
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值