[USACO06DEC]牛奶模式Milk Patterns

题目链接:戳我

我们知道后缀数组的h数组记录的是后缀i和后缀i-1的最长公共前缀长度,后缀的前缀其实就是子串。

因为是可以重复出现的子串,所以我们只要计算哪些h数组的长度大于等于x即可。这一步操作我们可以使用二分实现qwq

为什么可以二分呢?因为我们知道排名相邻越近,后缀相似度越高。然后因为我们要求的还是同一个子串出现的可重复的位置的个数,那么这个子串作为公共前缀,在后缀排序上一定是连续出现的qwq,那么就可以二分了。

代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,len,m,p,k;
int rnk[MAXN],tax[MAXN],h[MAXN],sa[MAXN],tp[MAXN],s[MAXN];
inline void qsort()
{
    for(int i=1;i<=m;i++) tax[i]=0;
    for(int i=1;i<=n;i++) tax[rnk[i]]++;
    for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
    for(int i=n;i>=1;i--) sa[tax[rnk[tp[i]]]--]=tp[i];
}
inline void suffix_sort()
{
    m=20001,p=0;
    for(int i=1;i<=n;i++) tp[i]=i,rnk[i]=s[i];
    qsort();
    for(int w=1;p<n;w<<=1,m=p)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=n-w+i;
        for(int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        qsort();
        swap(tp,rnk);
        rnk[sa[1]]=p=1;
        for(int i=2;i<=n;i++)
            rnk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&(tp[sa[i]+w]==tp[sa[i-1]+w]))?p:++p;
    }
}
inline void get_h()
{
    int j,k=0;
    for(int i=1;i<=n;i++)
    {
        if(k) k--;
        int j=sa[rnk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        h[rnk[i]]=k;
    }
    // for(int i=1;i<=n;i++) printf("rnk[%d]=%d\n",i,rnk[i]);
    // for(int i=1;i<=n;i++) printf("h[%d]=%d\n",i,h[i]);
}
inline bool check(int x)
{
    int cnt=0;
    for(int i=2;i<=n;i++)
    {
        if(h[i]<x) cnt=0;
        else cnt++;
        if(cnt>=k-1) return true;
    }
    return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&s[i]);
    suffix_sort();
    get_h();
    int l=1,r=n,ans;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)==true) ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fengxunling/p/10742306.html