HDU4080 Stammering Aliens

博客介绍了如何利用后缀数组和二分查找技术来解决HDU4080题目的算法。题目要求找到字符串中出现次数不少于m的最长子串,其长度及最后出现的位置。博主通过二分答案,预处理h数组并统计满足条件的子串数量,当数量大于等于m时找到解决方案,并特别考虑了m等于1的情况。
摘要由CSDN通过智能技术生成

后缀数组 二分

题目传送门

题目大意:给你一个数m和一个字符串s,求长度最大的子串ss满足它在s中的出现次数 m。输出ss的长度及最后出现的位置。

首先二分答案,设长度为x,预处理出h数组后,统计连续一段 h[i] h [ i ] ≥ x的i的个数。如果个数 m就满足了,并记录最后一个sa[i]。

还要特判一下m=1的情况。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 40005
using namespace std;
int n,m,k,lst,sa[N],rk[N],t[N],rs[N],h[N];
char s[N];
void RS(){
    memset(rs,0,sizeof(rs));
    for (int i=1;i<=n;i++) rs[rk[t[i]]]++;
    for (int i=1;i<=m;i++) rs[i]+=rs[i-1];
    for (int i=n;i>=1;i--) sa[rs[rk[t[i]]]--]=t[i];
}
void SA(){
    m=122,n=strlen(s+1);
    for (int i=1;i<=n;i++) rk[i]=s[i],t[i]=i; RS();
    for (int j=1,p=0;j<=n;j<<=1,p=0){
        for (int i=n-j+1;i<=n;i++) t[++p]=i;
        for (int i=1;i<=n;i++) if (sa[i]>j) t[++p]=sa[i]-j;
        RS(),memcpy(t,rk,sizeof(t)),p=1,rk[sa[1]]=1;
        for (int i=2,f;i<=n;i++){
            f=t[sa[i]]==t[sa[i-1]]&&t[sa[i]+j]==t[sa[i-1]+j];
            rk[sa[i]]=f?p:++p;
        }
        if (p==n) break; m=p;
    }
}
void mkh(){
    memset(h,0,sizeof(h));
    for (int i=1,k=0;i<=n;i++){
        if (k) k--;
        while (s[i+k]==s[sa[rk[i]-1]+k]) k++;
        h[rk[i]]=k;
    }
}
int pd(int x){
    lst=0; int sum=1,mx=0;
    for (int i=1;i<=n;i++){
        if (h[i]>=x) sum++,mx=max(mx,max(sa[i],sa[i-1]));
        else sum=1,mx=0;
        if (sum>=k) lst=max(lst,mx);
    }
    return lst;
}
int main(){
    while(~scanf("%d",&k)&&k){
        memset(s,0,sizeof(s));
        scanf("%s",s+1),SA(),mkh();
        if (k==1){
            printf("%d 0\n",strlen(s+1));
            continue;
        }
        int l=0,r=n,md,ans=0,x;
        while (l<=r)
            if (pd(md=l+r>>1)) ans=md,x=lst,l=md+1;
            else r=md-1;
        if (ans) printf("%d %d\n",ans,x-1);
        else puts("none");
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值