【BZOJ1717】[Usaco2006 Dec]Milk Patterns 产奶的模式 后缀数组

【BZOJ1717】[Usaco2006 Dec]Milk Patterns

Description

农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。

Input

* Line 1: 两个整数 N,K。

* Lines 2..N+1: 每行一个整数表示当天的质量值。

Output

* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度

Sample Input

8 2
1
2
3
2
3
2
3
1

Sample Output

4

题解:论文中的例题。

由于数的范围比较大,直接上基数排序会炸,所以本题可以用快排来搞,(但是我没写出来快排233),好吧于是我就先离散化,然后再用基数排序做的。

先二分答案,将height分成长度不小于ans的若干组,如果某一组的后缀个数不小于k,那么存在满足条件的长度为ans的子串。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=20010;
int n,m,K;
int ra[maxn],rb[maxn],r[maxn],sa[maxn],st[maxn],rank[maxn],h[maxn];
struct node
{
    int num,org;
}v[maxn];
bool cmp(node a,node b)
{
    return a.num<b.num;
}
int readin()
{
    int ret=0;    char gc;
    while(gc<'0'||gc>'9')    gc=getchar();
    while(gc>='0'&&gc<='9')    ret=ret*10+gc-'0',gc=getchar();
    return ret;
}
void work()
{
    int i,j,k,p,*x=ra,*y=rb;
    for(i=0;i<n;i++)    st[x[i]=r[i]]++;
    for(i=1;i<m;i++)    st[i]+=st[i-1];
    for(i=n-1;i>=0;i--)    sa[--st[x[i]]]=i;
    for(j=p=1;p<n;j<<=1,m=p)
    {
        for(p=0,i=n-j;i<n;i++)    y[p++]=i;
        for(i=0;i<n;i++)    if(sa[i]>=j)    y[p++]=sa[i]-j;
        for(i=0;i<m;i++)    st[i]=0;
        for(i=0;i<n;i++)    st[x[y[i]]]++;
        for(i=1;i<m;i++)    st[i]+=st[i-1];
        for(i=n-1;i>=0;i--)    sa[--st[x[y[i]]]]=y[i];
        for(swap(x,y),p=1,i=1,x[sa[0]]=0;i<n;i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
    }
    for(i=1;i<n;i++)    rank[sa[i]]=i;
    for(i=k=0;i<n-1;h[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int solve(int sta)
{
    int i,tot=1;
    for(i=1;i<n;i++)
    {
        if(h[i]<sta)    tot=0;
        if(++tot>=K)    return 1;
    }
    return 0;
}
int main()
{
    n=readin(),K=readin();
    int i;
    for(i=0;i<n;i++)    v[i].num=readin(),v[i].org=i;
    sort(v,v+n,cmp);
    int pre=-1,now=0;
    for(i=0;i<n;i++)
    {
        if(v[i].num>pre)    pre=v[i].num,now++;
        r[v[i].org]=now;
    }
    r[n++]=0;
    m=n;
    work();
    int L=1,R=n-1,mid;
    while(L<R)
    {
        mid=L+R>>1;
        if(solve(mid))    L=mid+1;
        else    R=mid;
    }
    printf("%d",L-1);
    return 0;
}
posted @ 2017-01-10 15:30  CQzhangyu  阅读(469)  评论(0编辑  收藏  举报