zoukankan      html  css  js  c++  java
  • 洛谷 P3879【TJOI2010】阅读理解

    题目链接:https://www.luogu.com.cn/problem/P3879

    题意

    有 $n$ 个句子,每句话里面有许多单词。

    有 $m$ 次询问,每次询问提供一个单词,问这个单词在以上哪些句子中出现。

    $n le 10^3$,$m le 10^4$,每个单词长度不超过 $20$,每个句子单词长度和不超过 $5 imes 10^3$。

    我的解法

    对于我这个如此之懒的人,肯定是不想写 Trie 这样的算法的,但又不想用 STL,所以就乱搞了个 hash。

    具体操作为:

    • 设置若干个质数作为模数(质数不能太大,最好不要超过 $2 imes 10^4$);
    • 设置 hash 需要用到的进制数;
    • 每个句子都开一个 bool 数组记录,将某个单词的 hash 值对模数取模后放入下标,并且赋值为 $1$
    • 将每个句子中的每个单词进行 hash,并进行上述操作;
    • 将提问的 $m$ 个单词分别进行 hash,并枚举前 $n$ 个句子中,是否满足 hash 对这些质数取模后的下标,在该句子 bool 数组中均被赋值过

    至于「若干个质数」到底是多少个,就需要进行一些尝试了。

    检验的办法有很多种,如将包含 map 的暴力与正解程序进行对拍。

    我经过多次尝试后,得到以下结果:

    • 设置 2 个质数检验:0 分;
    • 设置 4 个质数检验:30 分;
    • 设置 6 个质数检验:100 分。

    原因也很简单,一个字符串(可以当做 26 进制数)任意排布,得到的 hash 值会非常大。

    而我选择的质数都在 $10^4$ 上下,有非常大的几率发生 hash 冲突(可以说,如果只有一个模数,那每句话几乎都会发生冲突)。

    所以,设置尽可能多的模数,才能使得 hash 冲突发生的概率尽可能小。

    然后,就靠着这个办法,开 6 个质数检验的程序(不卡常)测试时间和只需要 208ms,在两千多份通过程序中是最快的。

    代码(以下为 7 个模数的版本,加了 register 跑 223ms,欢迎各路神仙用数据卡我)

    #include <cstring>
    #include <cstdio>
    #define INF 1e9
    #define eps 1e-6
    #define P 131
    #define MOD1 9967
    #define MOD2 8999
    #define MOD3 8893
    #define MOD4 11113
    #define MOD5 12197
    #define MOD6 12983
    #define MOD7 13121
    #define reg register
    typedef long long ll;
    typedef unsigned long long ull;
    
    int n, m, l[1010], len;
    char s[30];
    bool b1[1010][9999], b2[1010][9999], b3[1010][9999], b4[1010][11999];
    bool b5[1010][12999], b6[1010][12999], b7[1010][13999];
    ull Hash, num1, num2, num3, num4, num5, num6, num7;
    
    int main(){
    
        scanf("%d", &n);
        for(reg int i = 1; i <= n; i++){
            scanf("%d", &l[i]);
            for(reg int j = 1; j <= l[i]; j++){
                scanf("%s", s + 1), len = strlen(s + 1), Hash = 1;
                for(int k = 1; k <= len; k++)
                    Hash = Hash * P + (s[k] - 'a' + 1);
                b1[i][Hash % MOD1] = 1, b2[i][Hash % MOD2] = 1;
                b3[i][Hash % MOD3] = 1, b4[i][Hash % MOD4] = 1;
                b5[i][Hash % MOD5] = 1, b6[i][Hash % MOD6] = 1;
                b7[i][Hash % MOD7] = 1;
            }
        }
        scanf("%d", &m);
        for(reg int i = 1; i <= m; i++){
            scanf("%s", s + 1), len = strlen(s + 1), Hash = 1;
            for(reg int j = 1; j <= len; j++)
                Hash = Hash * P + (s[j] - 'a' + 1);
            num1 = Hash % MOD1, num2 = Hash % MOD2, num3 = Hash % MOD3;
            num4 = Hash % MOD4, num5 = Hash % MOD5, num6 = Hash % MOD6;
            num7 = Hash % MOD7;
            for(reg int j = 1; j <= n; j++)
                if(b1[j][num1] && b2[j][num2] && b3[j][num3] && b4[j][num4] && b5[j][num5] && b6[j][num6] && b7[j][num7])
                    printf("%d ", j);
            puts("");
        }
    
        return 0;
    }
  • 相关阅读:
    Docker 学习4 Docker容器虚拟化网络概述
    Ceph 命令
    Day_09【常用API】扩展案例1_程序中使用一个长度为3的对象数组,存储用户的登录名和密码……
    Day_08【面向对象】扩展案例4_年龄为30岁的老王养了一只黑颜色的2岁的宠物……
    Day_08【面向对象】扩展案例3_使用多态的形式创建缉毒狗对象,调用缉毒方法和吼叫方法
    Day_08【面向对象】扩展案例2_测试旧手机新手机类,并给新手机实现玩游戏功能
    Day_08【面向对象】扩展案例1_测试项目经理类和程序员类
    用两个栈实现队列
    二叉树前序、中序、后序遍历相互求法
    洗牌
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/13909167.html
Copyright © 2011-2022 走看看