[poj 3261] Milk Patterns:二分,哈希或后缀数组

来源:互联网 发布:js div添加class 编辑:程序博客网 时间:2024/05/18 08:10

题意:给一个N项的序列(1<=N <= 20,000,每一项是不超过1,000,000的自然数),求至少出现K次的子串的最大长度,出现位置允许重叠。

CS的模拟赛里的一题。题面里说subsequence,翻译过来是“子序列”。在我的理解中,这个“序列”和LCS问题里的序列一样,是从数列里抽的一根筋。很坑的wnxFinger_Leader同学先是说这是一道KMP,然后告诉我题面里“1 2 3 2 3 2 3 1 重复了2 3 2 3两次”,“两次”前面隐含着“至少”……

于是纠结了一个小时。

如果是子序列就不好做啦。至少,不具备单调性,二分是不可以的。

得知了正确题意,想到了哈希。因为先前的思考,潜意识里否定了二分。经过提醒,才发觉现在不是在做刚才那题啊……

二分最大长度L。验证的时候,取出所有长为L的子串的哈希值,排序,看是否有一个哈希值出现了至少K次。时间复杂度O(nlg2n)

马老师想到了二分+后缀数组,题解亦如此。但是感觉他俩说的又有点不一样……验证的时候遍历height数组,看是否有连续的一段长为K,且值均不小于L。时间复杂度O(nlgn)。传送门:WZH学长的题解

在OpenJudge上面,hash这个函数名CE了。

#include <cstdio>#include <algorithm>using namespace std;typedef unsigned long long ll;const int MAX_N = 1e6, H = 13131;int n, k, a[MAX_N];ll h[MAX_N+1], x[MAX_N+1] = {1};ll Hash(int i, int l){    return h[i] - h[i+l] * x[l];}bool check(int l){    static ll b[MAX_N];    int j = 0, ans = 0;    for (int i = 0; i+l <= n; ++i)        b[j++] = Hash(i, l);    sort(b, b+j);    for (int i = 0, t; i < j; i = t) {        t = i+1;        while (t < j && b[i] == b[t])            ++t;        ans = max(ans, t-i);    }    return ans >= k;}int main(){    scanf("%d %d", &n, &k);    for (int i = 0; i < n; ++i)        scanf("%d", &a[i]);    for (int i = 1; i <= n; ++i)        x[i] = x[i-1] * H;    for (int i = n-1; i >= 0; --i)        h[i] = h[i+1] * H + a[i];    int l = 1, r = n; // [l, r)    while (r-l > 1) {        int m = (l+r)/2;        if (check(m))            l = m;        else            r = m;    }    printf("%d\n", l);    return 0;}
0 0