洛谷 P1381 单词背诵 解题报告

P1381 单词背诵

题目描述

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

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

输入输出格式

输入格式:

第1行一个数\(n\)

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

接着是一个数\(m\)

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

输出格式:

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


首先看懂题,要先把出现的所有单词都背了才能再找最小连续。

所以第一目的是先做匹配找到有多少单词出现了。

\(hash\)\(map\)几种方法。不过我比较毒瘤,写了字典树匹配。

存下\(f[i]\)数组表示文章第\(i\)个单词是字典中的第几个单词。

第二步就是我也说不清楚的一个思想了,可能有单调队列的影子。

我们先固定左端点l,扫描右端点直到包含所有出现的单词至少一次。

用桶存下每个单词出现次数,然后边移动右端点边尝试移动左端点(如果加入右端点后出现次数大于1即可移动左端点)

我犯的智障错误:

  1. 字典树居然减了'0',真是绝了。。。
  2. 统计时不小心把0给统计了。。。

code:

#include <cstdio>
#include <cstring>
int min(int x,int y) {return x<y?x:y;}
const int N=1010;
const int M=100010;
const int inf=0x3f3f3f3f;
struct node
{
    int son[26],cnt;
}t[N*10];
char word[12];
int cnt=0,n,m,f[M];
void build(int j)
{
    scanf("%s",word);
    int now=0,len=strlen(word);
    for(int i=0;i<len;i++)
    {
        int k=word[i]-'a';
        if(t[now].son[k])
            now=t[now].son[k];
        else
        {
            t[now].son[k]=++cnt;
            now=cnt;
        }
    }
    t[now].cnt=j;
}
void check(int j)
{
    scanf("%s",word);
    int len=strlen(word),now=0;
    for(int i=0;i<len;i++)
    {
        int k=word[i]-'a';
        if(t[now].son[k])
            now=t[now].son[k];
        else
            return;
    }
    f[j]=t[now].cnt;
}
int cnt0[N],used[N],l,a,b=inf,a0=0;//要背长度,最短连续段长度
void DP()
{
    used[0]=1;
    for(int i=1;i<=m;i++)
    {
        if(!used[f[i]])
        {
            a0++;
            used[f[i]]=1;
        }
    }
    printf("%d\n",a0);
    if(a0==0) {printf("0\n");return;}
    l=1;
    memset(used,0,sizeof(used));
    used[0]=1;
    for(int i=1;i<=m;i++)
    {
        cnt0[f[i]]++;
        while(cnt0[f[l]]>1)
        {
            cnt0[f[l]]--;
            l++;
        }
        if(!used[f[i]])
        {
            a++;
            used[f[i]]=1;
        }
        if(a==a0)
            b=min(i+1-l,b);
    }
    printf("%d\n",b);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        build(i);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        check(i);
    DP();
    return 0;
}


2018.6.3

posted @ 2018-06-03 19:48  露迭月  阅读(222)  评论(0编辑  收藏  举报