题目描述
灵梦有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;
题解:
本题真是显示出了pb_ds库功能的强大。不懂这个库的可看一下这篇文章:
http://www.cnblogs.com/huihao/p/7137250.html
思路一:
先解决第一问:用两个哈希表一个记录必背单词,一个记录这个但是是否被背过。
第二问我们发现最多要背单词的最短的连续段的长度满足单调性,我们就可以用一个队列来实现,在队列中记录。注意数据中有一个点要特判,因为最多包含的要背的单词数是0,循环会死在里面的。
我首先想到的使用STL库中的set写,因为要保证每个单词的唯一性。
#include<iostream> #include<cstdio> #include<cstring> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/hash_policy.hpp> #include<set> using namespace std; using namespace __gnu_pbds; gp_hash_table<string,bool>h1; gp_hash_table<string,bool>h2; gp_hash_table<string,int>h3; int n,m,num; char s[101001][20]; set<string> a; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",s[i]); h1[s[i]]=1; } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",s[i]); if(h1[s[i]]&&!h2[s[i]]) { num++; h2[s[i]]=1; } } int l=1,minn=100000000; for(int i=1;i<=m;i++) { if(h1[s[i]]) {a.insert(s[i]);h3[s[i]]++;} int tmp=a.size(); while(tmp==num) { minn=min(minn,i-l+1); if(h1[s[i]]) { h3[s[l]]--; if(h3[s[l]]==0) {a.erase(s[l]);tmp--;} } l++; } } printf("%d %d ",num,minn); return 0; }
思路二;
但后来发现完全没这个必要,因为set的效率有点低,如果不断地插入删除,数据小的话有可能过,数据大的话就卡死了。所以我们可以在开个哈希表记录每个必背单词在队列从中出现的次数。
#include<iostream> #include<cstdio> #include<cstring> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/hash_policy.hpp> using namespace std; using namespace __gnu_pbds; cc_hash_table<string,bool>h1; cc_hash_table<string,bool>h2; cc_hash_table<string,int>h3; int n,m,num; char s[120001][20]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",s[i]); h1[s[i]]=1; } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",s[i]); if(h1[s[i]]&&!h2[s[i]]) { num++; h2[s[i]]=1; } } int l=1,minn=100000000; int cnt=0; for(int i=1;i<=m;i++) { if(h1[s[i]]) {if(!h3[s[i]])cnt++;h3[s[i]]++;} while(cnt==num) { if(l==i) {minn=0;break;} minn=min(minn,i-l+1); if(h1[s[l]]) { h3[s[l]]--; if(h3[s[l]]==0) cnt--; } l++; } } printf("%d %d ",num,minn); return 0; }
思路三:
哈希+二分,经过优化之后貌似快了一些。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,e,wo; char x[13]; int b[10003]; unsigned long long y[100005]; unsigned long long q[15]; unsigned long long g[1006],t[100005]; inline unsigned long long has(char ss[13]) { unsigned long long tot=0; for(int i=strlen(ss)-1;i>=0;i--) { tot+=(ss[i]-'a'+1)*q[i]; } return tot; } bool ef(int l,int r,unsigned long long k) { e=(l+r)>>1; if(k==t[e]) return 1; if(l==r) return 0; if(k<t[e]) return ef(l,e,k); else return ef(e+1,r,k); } int ff(int l,int r,unsigned long long k) { e=(l+r)>>1; if(k==g[e]) return e; if(l==r) return 0; if(k<g[e]) return ff(l,e,k); else return ff(e+1,r,k); } void init() { q[1]=1; for(int i=2;i<=13;i++) q[i]=q[i-1]*27; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",&x); g[i]=has(x); } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",&x); t[i]=has(x); y[i]=t[i]; } } int main() { int tot=0,z; init(); sort(t,t+m+1); for(int i=1;i<=n;i++) if(ef(1,m,g[i])) tot++; printf("%d ",tot);wo=tot; int l=0,r=0;int mi=m; sort(g,g+n+1); if(tot==0) mi=0; else while(1) { if(wo==0) { if(mi>r-l) mi=r-l; l++; z=ff(1,n,y[l]); if(z!=0) {b[z]--;if(b[z]==0) wo++;} } else { if(r==m) break; r++; z=ff(1,n,y[r]); if(z!=0) {b[z]++;if(b[z]==1) wo--;} } } printf("%d ",mi); return 0; }