洛谷 P1381 單詞背誦 解題報告
阿新 • • 發佈:2018-06-03
一段 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即可移動左端點)
我犯的智障錯誤:
- 字典樹居然減了‘0‘,真是絕了。。。
- 統計時不小心把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 單詞背誦 解題報告