1. 程式人生 > >洛谷 P1381 單詞背誦 解題報告

洛谷 P1381 單詞背誦 解題報告

一段 math string i+1 HA mem 找到 嘗試 還要

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

洛谷 P1381 單詞背誦 解題報告