【bzoj1717】[Usaco2006 Dec]Milk Patterns 产奶的模式 后缀数组+离散化

题目描述

农夫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。

输入

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

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

输出

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

样例输入

8 2
1
2
3
2
3
2
3
1

样例输出

4


题解

后缀数组+离散化

题目中m比较大,需要离散化。

对于这类求重复字串长度的问题,正着做比较难做。

我们可以先二分答案,然后将问题转化为是否有k次重复的字串。

假设有k次重复的字串,则一定有连续的k个height大于等于mid。

那么我们就可以在O(n)的时间内进行judge操作,总时间复杂度为O(nlogn),可以通过。

另:这里的judge写法比较特殊。由于height[n]是没有值的,所以一般情况下一定能进行下一步判断。但如果m=0,无论如何也无法进行,所以加上特判。

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 20005
int ws[N] , wv[N] , wa[N] , wb[N] , sa[N] , r[N] , n , m , k;
int rank[N] , height[N];
struct data
{
    int num , rn , tnum;
}a[20001];
bool cmp1(data a , data b)
{
    return a.num < b.num;
}
bool cmp2(data a , data b)
{
    return a.rn < b.rn;
}
void da()
{
    int i , j , p , *x = wa , *y = wb , *t;
    for(i = 0 ; i < m ; i ++ ) ws[i] = 0;;
    for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ;
    for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
    for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[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 >= 0) y[p ++ ] = sa[i] - j;
        for(i = 0 ; i < n ; i ++ ) wv[i] = x[y[i]];
        for(i = 0 ; i < m ; i ++ ) ws[i] = 0;
        for(i = 0 ; i < n ; i ++ ) ws[wv[i]] ++ ;
        for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1];
        for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[wv[i]]] = y[i];
        for(t = x , x = y , y = t , x[sa[0]] = 0 , i = p = 1 ; i < n ; i ++ )
        {
            if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j])
                x[sa[i]] = p - 1;
            else
                x[sa[i]] = p ++ ;
        }
    }
    for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i;
    for(i = p = 0 ; i < n - 1 ; height[rank[i ++ ]] = p)
        for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ );
}
bool judge(int m)
{
    if(m == 0) return 1;
    int i , last = 0;
    for(i = 1 ; i <= n ; i ++ )
    {
        if(height[i] < m)
        {
            if(i - last >= k) return 1;
            last = i;
        }
    }
    return 0;
}
int main()
{
    int i , le , ri , mi , ans = 0;
    scanf("%d%d" , &n , &k);
    for(i = 0 ; i < n ; i ++ )
    {
        scanf("%d" , &a[i].num);
        a[i].rn = i;
    }
    sort(a , a + n , cmp1);
    for(i = 0 ; i < n ; i ++ )
    {
        if(i == 0 || (i > 0 && a[i].num > a[i - 1].num))
            m ++ ;
        a[i].tnum = m;
    }
    sort(a , a + n , cmp2);
    for(i = 0 ; i < n ; i ++ )
        r[i] = a[i].tnum;
    r[n ++ ] = 0;
    m ++ ;
    da();
    le = 0;
    ri = n;
    while(le <= ri)
    {
        mi = (le + ri) >> 1;
        if(judge(mi))
        {
            ans = mi;
            le = mi + 1;
        }
        else ri = mi - 1;
    }
    printf("%d\n" , ans);
    return 0;
}
posted @ 2017-01-10 15:30  GXZlegend  阅读(294)  评论(0编辑  收藏  举报