洛谷 P1381 单词背诵 题解
免责声明:此题解代码来自于louhao088的文章
完整题目
P1381 单词背诵
题目描述
灵梦有 n n n 个单词想要背,但她想通过一篇文章中的一段来记住这些单词。
文章由 m m m 个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要背的单词(重复的只算一个)。并且在背诵的单词量尽量多的情况下,还要使选出的文章段落尽量短,这样她就可以用尽量短的时间学习尽可能多的单词了。输入格式
第 1 1 1 行一个数 n n n,接下来 n n n 行每行是一个长度不超过 10 10 10 的字符串,表示一个要背的单词。
接着是一个数 m m m,然后是 m m m 行长度不超过 10 10 10 的字符串,每个表示文章中的一个单词。输出格式
输出文件共 2 2 2 行。第 1 1 1 行为文章中最多包含的要背的单词数,第 2 2 2 行表示在文章中包含最多要背单词的最短的连续段的长度。
输入输出样例 #1
输入 #1
3 hot dog milk 5 hot dog dog milk hot输出 #1
3 3说明/提示
数据规模与约定
- 对于 30 % 30\% 30% 的数据, n ≤ 50 n \le 50 n≤50, m ≤ 500 m \le 500 m≤500;
- 对于 60 % 60\% 60% 的数据, n ≤ 300 n \le 300 n≤300, m ≤ 5000 m \le 5000 m≤5000;
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1≤n≤1000, 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1≤m≤105。
1. 读题
题说的很清楚,两个目标:
-
在文章中找到一个连续的段落(即子数组),使得该段落中包含的不同“要背的单词”数量尽可能多。
- 重复出现的要背单词只算一次。
-
在所有满足“包含最多不同要背单词”的段落中,选择长度最短的一个,输出其长度。
- 段落长度定义为该连续段落的单词数量。
2. 解题思路
用数据结构“map”来记录单词的出现个数和需要背的单词,然后就比较简单了,用双指针滑动窗口的方式解决。
3. 代码片段分析
- 变量、数据结构定义。
#include<bits/stdc++.h>
using namespace std;
map<string,int>sum;//用于记录单词的出现个数量。
map<string,bool>flag;//用于记录需要背的单词。
int ans[2],n,m,l=1,r=1;//与题目中的相同。
//l:左指针。
//r:右指针。
string s[100001];//用于读入文章。
- 数据读入。
cin>>n;
for(int i=1;i<=n;i++){
string s1;
cin>>s1;
flag[s1]=true;//这些单词需要背。
}
- 双指针遍历。
cin>>m;
for(r=1;r<=m;r++){
cin>>s[r];//读入文章。
if(flag[s[r]])sum[s[r]]++;//记录s[r]这个单词出现的个数。
if(sum[s[r]]==1){//如果这个单词只出现了1次(特判)。
ans[0]++;
ans[1]=r-l+1;//"r-l+1"指的是在文章中包含最多要背单词的最短的连续段的长度(窗口长度)。
}
while(l<=r){//双指针遍历部分。
if(!flag[s[l]]){//这个单词不需要背。
l++;//判断下一个。
continue;
}
if(sum[s[l]]>=2){//当前单词在这一段中的出现次数大于等于2(说明就算右移这一段依然包含这个单词)。
sum[s[l]]--;
l++;
continue;
}
break;
}
ans[1]=min(ans[1],r-l+1);//ans[1]始终保存窗口长度。
}
cout<<ans[0]<<endl<<ans[1];
return 0;
4. AC代码
#include<bits/stdc++.h>
using namespace std;
map<string,int>sum;
map<string,bool>flag;
int ans[2],n,m,l=1,r=1;
string s[100001];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
string s1;
cin>>s1;
flag[s1]=true;
}
cin>>m;
for(r=1;r<=m;r++){
cin>>s[r];
if(flag[s[r]])sum[s[r]]++;
if(sum[s[r]]==1){
ans[0]++;
ans[1]=r-l+1;
}
while(l<=r){
if(!flag[s[l]]){
l++;
continue;
}
if(sum[s[l]]>=2){
sum[s[l]]--;
l++;
continue;
}
break;
}
ans[1]=min(ans[1],r-l+1);
}
cout<<ans[0]<<endl<<ans[1];
return 0;
}
5. 例题推荐
- 洛谷 P1886 滑动窗口 /【模板】单调队列(滑动窗口板子题)
- 洛谷 P1638 逛画展(双指针板子题)
如果对你有帮助,点个赞再走吧!谢谢!
488

被折叠的 条评论
为什么被折叠?



