poj3261 Milk Patterns【二分答案+后缀数组】

本文介绍了一种使用后缀数组解决寻找给定数字串中可重叠至少出现k次的最长重复子串的方法。通过建立后缀数组并结合二分查找技术,可以有效地找到满足条件的最长重复子串。
题目大意:

给定一个长度为n的数字串,问可重叠至少出现k次的最长重复子串的长度。

解题思路:

做法有很多,比如二分+哈希+map(或哈希表)就比较清新,这里说说后缀数组的做法。
建立后缀数组,二分ans后只需看height是否有连续k-1个大于ans即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=20005,M=1000005;
int n,m,k,a[N],rank[N],sa[N],tp[N],c[M],height[N];

void Rsort()
{
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[rank[tp[i]]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i;i--)sa[c[rank[tp[i]]]--]=tp[i];
}

void SA_init()
{
    m=1000000;
    for(int i=1;i<=n;i++)rank[i]=a[i],tp[i]=i;
    Rsort();
    for(int w=1;w<n;w<<=1)
    {
        int j=0;
        for(int i=n-w+1;i<=n;i++)tp[++j]=i;
        for(int i=1;i<=n;i++)if(sa[i]>w)tp[++j]=sa[i]-w;
        Rsort();
        for(int i=1;i<=n;i++)swap(rank[i],tp[i]);
        rank[sa[1]]=j=1;
        for(int i=2;i<=n;i++)
            rank[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w]?j:++j);
        m=j;
    }
    int w=0,j;
    for(int i=1;i<=n;height[rank[i++]]=w)
        for(w=w?w-1:w,j=sa[rank[i]-1];a[i+w]==a[j+w];w++);
}

bool check(int len)
{
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
        if(height[i]>=len)
        {
            cnt++;
            if(cnt>=k)return true;
        }
        else cnt=1;
    }
    return false;
}


int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),k=getint();
    for(int i=1;i<=n;i++)a[i]=getint();
    SA_init();
    int l=0,r=n;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    printf("%d",r);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值