• 【luogu P1381单词背诵】题解


    单词背诵

    题目描述

    灵梦有n个单词想要背,但她想通过一篇文章中的一段来记住这些单词。

    文章由m个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要背的单词(重复的只算一个)。并且在背诵的单词量尽量多的情况下,还要使选出的文章段落尽量短,这样她就可以用尽量短的时间学习尽可能多的单词了。

    输入格式

    第1行一个数n,

    接下来n行每行是一个长度不超过10的字符串,表示一个要背的单词。

    接着是一个数m,

    然后是m行长度不超过10的字符串,每个表示文章中的一个单词。

    输出格式

    输出文件共2行。第1行为文章中最多包含的要背的单词数,第2行表示在文章中包含最多要背单词的最短的连续段的长度。

    输入输出样例

    输入 #1
    3
    hot
    dog
    milk
    5
    hot
    dog
    dog
    milk
    hot
    
    输出 #1
    3
    3
    

    说明/提示

    【数据范围】

    对于30%的数据 n<=50,m<=500;

    对于60%的数据 n<=300,m<=5000;

    对于100%的数据 n<=1000,m<=100000;

    题目意思:

    给出两个单词集。

    1.问集合1中的单词,有多少个在集合2中出现。

    2.求在满足第一问的最多出现次数的情况下,集合2中最短的连续子集。

    分析:

    由于字符处理起来不是很方便,因此我们使用哈希将字符串转化成数字,这样处理起来会比字符串方便的多。

    当然也可以使用 map 来存。

    对于第一问比较简单。

    用一个bool 数组v1[] 0/1 记录这个单词是否在集合1中出现过(1:是,0:否);

    然后对集合2进行判断。

    难点在第二问。

    n的规模比较大,可以想到基本是需要线性的了。

    我提供一种单调队列做法。

    首先申明几个变量:

    1. lst [ 哈希值(s) ] 表示处理到当前字符串,字符串s上一次出现的位置在哪里。 

    2. num 计数器,表示处理到当前字符串,有贡献的字符串个数,即为出现在集合1里的字符串个数(相同算一次)。

    3. v2[ 哈希值(s) ] 表示s是否出现在集合1和集合2中,若没有,则无需考虑这个字符串。

    可以明确得到,当num=ans1时,更新一次答案。

    接下来最重要的就是单调队列里面放什么?

    我们放进去的是每个有贡献的字符串 b[i] 。

    满足单调性的是他们的位置。

    如果下一个相同的字符串出现了,就可以把它弹掉了。

    这样当我们统计答案时(num==ans1)

    我们用当前位置 i 减去队列首元素的位置+1,就是这一段满足条件区间长度。

    最后特判一下0.

    #include<iostream>
    #include<cstdio> 
    #include<cstring>
    using namespace std;
    const int N=1e6,P=1e7+9,inf=1e9;
    int n,m,ans1,ans2=inf;
    int a[N],b[N];
    bool v1[P+1],v2[P+1],vis[P+1];
    int lst[P],q[N];
    char s[20];
    inline int hash(char *s){//哈希函数
        int len=strlen(s);
        long long res=0;
        for(int i=0;i<len;i++){
            res*=31;
            res+=s[i]-'a';
            res%=P;
        }
        return res;
    }
    int main()
    {
        int i,j;
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%s",s);
            a[i]=hash(s);
            v1[a[i]]=1;//v1[] 记录是否在集合1中出现
        }
        scanf("%d",&m);
        for(i=1;i<=m;i++){
            scanf("%s",s);    
            b[i]=hash(s);
            if(v1[b[i]]){
                if(!v2[b[i]]){
                    v2[b[i]]=1;
                    ans1++;
                }
            }
        }
        int num=0,head=1,tail=0;
        for(i=1;i<=m;i++){
            if(lst[b[i]]==0&&v2[b[i]]) num++;
            if(v2[b[i]]){//如果!v2[]一定不会有贡献
                lst[b[i]]=i;
                q[++tail]=i;
            }
            while(head<=tail&&q[head]<lst[b[q[head]]]) head++;
            if(num==ans1) ans2=min(ans2,i-q[head]+1);//更新答案
        }
        if(ans1) printf("%d
    %d",ans1,ans2);
        else printf("0
    0");
    }
  • 相关阅读:
    HangFire快速入门
    HangFire概述
    Lodop错误汇总
    微信支付过程遇到的问题
    动态规划算法
    几句话~
    Welcom To My Blog
    【技巧】关于素数
    安徽省小学组省赛2014年第一题 木板面积(C++)
    洛谷 P1425 小鱼的游泳时间
  • 原文地址:https://www.cnblogs.com/quitter/p/11774334.html
Copyright © 2020-2023  润新知