SP687 REPEATS

https://www.luogu.com.cn/problem/SP687

题意:

求重复出现次数最多的连续重复子串

枚举循环长度i

把整个串每i个长度划分一段,位置1+k*i 是每段的起点,称他为分割点 

若一个长为i的子串连续出现至少2次,那么它至少包含了2个连续的分割点、

我们计算相邻两个分割点的后缀的最长公共前缀L

可以得到重复次数为 L/i + 1

但是有一个问题,L/i + 1 是重复子串的起点在分割点,我们不能保证重复子串的起点一定在分割点,即分割点把一个循环节断开,导致计算结果少了1

L%i 是以分割点为起点的重复子串,在重复了 L/i + 1次之后,剩下的长度

那么 i - L%i 就可以看作是分割点前面可以补充的长度

再计算 这相邻的两个分割点前 i - L%i  位置 的后缀的最长公共前缀

如果大于等于i,那么重复次数再加1

求两个后缀的最长公共前缀,在height数组上用st表即可

#include<bits/stdc++.h>

using namespace std;

#define N 50005

char ch[N];
int n,k,a[N],v[N],p,q,sa[2][N],rk[2][N],h[N];

int f[N][20];

void mul(int *sa,int *rk,int *SA,int *RK)
{
    for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
    for(int i=n;i;i--)
        if(sa[i]>k) 
            SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++) 
        SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++) 
        RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
}
void presa()
{
    p=0;
    q=1;
    for(int i=1;i<=26;++i) v[i]=0;
    for(int i=1;i<=n;i++) v[a[i]]++;
    for(int i=1;i<=26;i++) v[i]+=v[i-1];
    for(int i=1;i<=n;i++) 
        sa[p][v[a[i]]--]=i;
    for(int i=1;i<=n;i++) 
        rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]);
    for(k=1;k<n;k<<=1,swap(p,q))
        mul(sa[p],rk[p],sa[q],rk[q]);
    for(int i=1,k=0;i<=n;i++)
    {
        int j=sa[p][rk[p][i]-1];
        while(a[i+k]==a[j+k]) k++;
        h[rk[p][i]]=k;if(k) k--;
    }
}

void prest()
{
    for(int i=2;i<=n;++i) f[i][0]=h[i];
    for(int i=1;1<<i<=n;++i)
        for(int j=2;j+(1<<i)-1<=n;++j)
            f[j][i]=min(f[j][i-1],f[j+(1<<i-1)][i-1]); 
}

int getlcp(int s,int t)
{
    s=rk[p][s];
    t=rk[p][t];
    if(s<t) ++s;
    else 
    {
        swap(s,t);
        ++s;
    }
    int l=t-s+1;
    int m=log(l)/log(2);
    return min(f[s][m],f[t-(1<<m)+1][m]);
}

void solve()
{
    int ans=1,sum,lcp,tmp;
    prest();
    for(int i=1;i<n;++i)
    {
        for(int j=1;j+i<=n;j+=i)
        {
            lcp=getlcp(j,j+i);
            sum=lcp/i+1;
            tmp=lcp%i;
            if(tmp)
            {
                if(j-(i-tmp)>0)
                {
                    lcp=getlcp(j-(i-tmp),j+i-(i-tmp));
                    if(lcp>=i) ++sum;
                }
            }
            ans=max(ans,sum);
        }
    }
    printf("%d
",ans);
}

int main()
{
    int T;
    char ss[3];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
        {
            scanf("%ss",ss);
            a[i]=ss[0]-'a'+1;
        }
        presa();
        solve();
    }
}
作者:xxy
本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15141477.html