洛谷2852 [USACO06DEC]牛奶模式Milk Patterns(Hash)(二分)(排序)

题目

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

题解1

后缀数组
后缀数组是个好东西,可惜我不懂
看题时,确实是一道求重复次数不小于k的最长子串的问题,是“后缀数组的裸题”?。

题解2

hash
Hash也可以?只要有字符串是否相同的问题,hash就可以发挥它的作用。
二分模式的长度len。判断时,把长度为len的模式的hash值全部存入h数组进行排序,其作用是计算有多少个同hash值相同的模式(即重复的模式),不小于k则返回true。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int maxn=20010;
const ull P=131;ull power[maxn];

int n,k;
int a[maxn];
ull hash[maxn];

ull H(int l,int r)
{
    return hash[r]-hash[l-1]*power[r-(l-1)];
}

ull h[maxn];
bool check(int len)//判断是否有重复不小于k次的模式
{
    for(int r=len;r<=n;r++)
    {
        h[r]=H(r-len+1,r);
    }
    sort(h+len,h+n+1);
    int cnt=1;//统计重复次数
    for(int i=len+1;i<=n;i++)
    {
        if(h[i]==h[i-1])
        {
            cnt++;
            if(cnt>=k) return true;
        }
        else cnt=1;
    }
    return false;
}

int main()
{
    scanf("%d%d",&n,&k);
    power[0]=1;hash[0]=0;
    for(int i=1;i<=n;i++)
    {
        power[i]=power[i-1]*P;
        scanf("%d",&a[i]);
        hash[i]=hash[i-1]*P+a[i];
    }
    
    int l=1,r=n,ans;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/81516228