洛谷 P1381 单词背诵 题解

洛谷 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 n50 m ≤ 500 m \le 500 m500
  • 对于 60 % 60\% 60% 的数据, n ≤ 300 n \le 300 n300 m ≤ 5000 m \le 5000 m5000
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1 \le n \le 1000 1n1000 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1m105

1. 读题

题说的很清楚,两个目标:

  • 在文章中找到一个连续的段落(即子数组),使得该段落中包含的不同“要背的单词”数量尽可能多。

    • 重复出现的要背单词只算一次。
  • 在所有满足“包含最多不同要背单词”的段落中,选择长度最短的一个,输出其长度。

    • 段落长度定义为该连续段落的单词数量。

2. 解题思路

用数据结构“map”来记录单词的出现个数需要背的单词,然后就比较简单了,用双指针滑动窗口的方式解决。


3. 代码片段分析

  1. 变量、数据结构定义。
#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];//用于读入文章。
  1. 数据读入。
	cin>>n;
	for(int i=1;i<=n;i++){
		string s1;
		cin>>s1;
		flag[s1]=true;//这些单词需要背。
	}
  1. 双指针遍历。
	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. 例题推荐


如果对你有帮助,点个赞再走吧!谢谢!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

—海燕—

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值