题解 P1381 单词背诵(哈希,队列)

Posted wxy-max

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1381 单词背诵(哈希,队列)相关的知识,希望对你有一定的参考价值。

第一问(哈希)

要使得包含单词最多,那么就需将文章中所有在单词表中出现过的单词全部统计。

而一个字符一个字符的比对复杂度过高,因此我们需要求出单词表与文章的字符串哈希,这样我们就可以(O(1))比较了。而哈希之后的值是一个整数,那么我们目前的问题就转化为有两个数组(A[])(B[])(A[])数组中有多少个不同的数在(B[])中出现过。那么对于这个问题很显然我们可以建立一个(bool)数组(C[])(bool)数组(used[]),扫 一遍(A[]),以(A[])中的每个元素为下标,赋值为(true)。扫一遍(B[]),如果(C[B[i]]==true)(used[B[i]]=false)那么(cnt++)(used[B[i]])赋值为(true)

那么很显然我们就可以用哈希值作为(C[])数组的下标(要保证在求哈希之模的数能作为数组下标)愉快的解决第一个问。

    cin >> n;
    for (int i = 0; i < n; i++){
        cin >> s;
        have[hsh(s)] = true;
    }
    cin >> m;
    for (int i = 1; i <= m; i++){
        cin >> s;
        val[i] = hsh(s);
        if (have[val[i]] && !used[val[i]]){
            cnt++;
            used[val[i]] = true;
        }
    }

此步复杂度为(O(m))

第二问

第二问暴力部分分

(O(m^3))暴力枚举

我们可以枚举长度(len)并分别判断从第(i)个单词开始为第一个单词的长度为(len)子串是否合法(即在(B[])中存在连续的长度为(k)的一段,包含(cnt)个单词)。其中(1≤len≤m),判断的复杂度为(O(m^2))

因此总复杂度为(O(m^3))

(O(m^2 log m))暴力二分

在上面所描述的暴力中,我们将第二问转化为了一个判定问题。

而在我们所枚举的集合很显然是具有单调性的:

如果答案为(k)

那么如果长度大于(k)必然合法

而长度要是小于(k)则必然不合法

因此我们可以二分长度(len)(check)的复杂度为(O(m^2)),二分的复杂度为(O(log m))因此总复杂度为(O(m^2 log m))

(O(m))正解

(f_{l,r})为从(B[l])(B[r])这段区间内,包含(A[])中的数的个数。

(f_{l,r}=cnt)(C[B[r]]=true)并满足于(C[B[r+1]]=false)

设其中包含(cnt)(A[])中元素个数的最短区间为([a,b])(等价于([a,r])

那么显然(f_{a,b}=f_{l,r}=f_{a,r})

那么我们则可以用(r - a + 1)更新元素

我们维护一个集合(S)(S‘)(S)包括(B[l] sim B[r])(S‘)包括(B[a] sim B[r])

那么((r-l+1)=|S|)((r-a+1)=|S‘|)

我们已知(l,r)与集合(S),接下来即求出(a)(S‘)

因为(f_{l,r}=f_{a,r}=cnt)([a,r])为包含(cnt)(A[])中元素个数的最短区间

那么(f_{a+1,r})必然小于(cnt)

(f_{l,r-1})必然小于(cnt)

([a,r])为连续的一段

因此我们得到(S‘)最朴素的做法即从(l)开始依次删除(B[i])所对应的集合的元素。对应的区间为([c,r])(f_{c,r}<cnt)(r-c+2)即为(r - a + 1)

因此我们可以枚举(r),计算(f_{1,r})并维护集合(S),每次枚举插入(B[r])

(f_{1,r}=cnt)时依次删除前(i)个元素,直到(f_{1,r}<cnt)

并用(|S‘|+1)更新答案。

而对于集合(S),因为我们只在一端插入,一端删除,因此可以用队列维护。

而对于每个元素最多进队(1)次出队(1)次。

因此复杂度为(O(m))

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int mod = 1000007;
const int base = 131;
bool have[mod], used[mod];
int val[100005],sum[mod];
char s[15];
queue<int> q;

int hsh(char* s){
    int ret = 0, len = strlen(s);
    for (int i = 0; i < len; i++){
        ret = (ret * base + s[i]) % mod;
    }
    return ret;
}

int main() {
    int n, m, cnt = 0, len, now;
	cin >> n;
    for (int i = 0; i < n; i++){
        cin >> s;
        have[hsh(s)] = true;
    }
    cin >> m;
    for (int i = 1; i <= m; i++){
        cin >> s;
        val[i] = hsh(s);
        if (have[val[i]] && !used[val[i]]){
            cnt++;
            used[val[i]] = true;
        }
    }
    if (cnt == 0){
        cout << 0 << endl;
        cout << 0 << endl;
    }else{
        len = m;
        now = 0;
        for (int i = 1; i <= m; i++){
            int queuelen = q.size();
            q.push(i);
            sum[val[i]]++;
            if (have[val[i]] && sum[val[i]] == 1) now++;
            if (now == cnt){
                while (now == cnt){
                    int v = q.front();
                    q.pop();
                    sum[val[v]]--;
                    if (sum[val[v]] == 0 && have[val[v]]) now--;
                }
                queuelen = q.size() + 1;
                len = min(len,queuelen);
            } 
        }
        cout << cnt << endl << len;
    }
    return 0;
}

以上是关于题解 P1381 单词背诵(哈希,队列)的主要内容,如果未能解决你的问题,请参考以下文章

题解单词背诵

单词背诵CodeVS3013 哈希

CODEVS——T 3013 单词背诵

android项目实战-背呗单词DEV06-单词背诵实现

单词背诵

[CODEVS3031] 单词背诵 - 字符串hash