【POJ1743】—Musical Theme(后缀数组)

本文介绍了一种使用后缀数组和二分查找解决字符串问题的方法,具体为寻找一个字符串中长度超过4且出现2次以上的最长重复子串。通过将原字符串转化为相邻字符的差值序列,构建后缀数组,并利用二分查找确定最长重复子串的长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

大意:给你一个字符串,求长度超过4、出现2次以上的最长串的长度

首先取相邻的差作为字符串

对这个串建立后缀数组之后

我们二分枚举这个最长串的长度

把所有相邻的hththt大于midmidmid化作一组

取其中的最大最小值的差

如果存在一组内的差大于midmidmid,返回TrueTrueTrue,反之FalseFalseFalse

代码:

#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
const int N=20005;
const int inf=100000000;
int n,m,rk[N],sa[N],a[N],p[N],sa2[N],cnt[N],ht[N];
inline void bucket_sort(){
    for(int i=1;i<=m;i++)cnt[i]=0;
    for(int i=1;i<=n;i++)++cnt[rk[sa2[i]]];
    for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
    for(int i=n;i>0;i--)sa[cnt[rk[sa2[i]]]--]=sa2[i];
}
inline void init(){
    for(int i=1;i<=n;i++)rk[i]=a[i],sa2[i]=i;
    bucket_sort();
    for(int i=1,pos=0;i<=n&&pos<n;i<<=1){
        pos=0;
        for(int j=n-i+1;j<=n;j++)sa2[++pos]=j;
        for(int j=1;j<=n;j++)if(sa[j]>i)sa2[++pos]=sa[j]-i;
        bucket_sort();
        swap(rk,sa2);
        rk[sa[1]]=1,pos=1;
        for(int j=2;j<=n;j++)rk[sa[j]]=((sa2[sa[j]]==sa2[sa[j-1]])&&(sa2[sa[j]+i]==sa2[sa[j-1]+i])?pos:++pos);
        m=pos;
    }
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1,k=0,j;i<=n;ht[rk[i++]]=k){
        for(k?k--:0,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
    }
}
inline bool check(int k){
    int minn=inf,maxn=-inf;
    for(int i=1;i<=n;i++){
        if(ht[i]<k){maxn=-inf,minn=inf;continue;}
        maxn=max(maxn,max(sa[i],sa[i-1])),minn=min(minn,min(sa[i],sa[i-1]));
        if(maxn-minn>k)return true;
    }
    return false;
}
int main(){
    n=read();
    while(n){
        for(int i=1;i<=n;i++)p[i]=read();
        --n,m=176;
        for(int i=1;i<=n;i++)a[i]=p[i+1]-p[i]+88;
        init();
        int l=4,r=n,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid))l=mid+1,ans=mid;
            else r=mid-1;
        }
        if(!ans)ans=-1;
        ans++;
        cout<<ans<<'\n';
        n=read();
    }
}

转载于:https://www.cnblogs.com/stargazer-cyk/p/10366375.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值