【洛谷】哈希表实战:5 道经典算法题(unordered_map/set 应用 + 避坑指南)
文章目录
- 学籍管理
- 不重复数字
- 阅读理解
- A-B数对
- CitiesandStatesS
学籍管理
题目描述
题目解析
本题比较简单,map和unordered_map都可以,unordered_map更优。主要是需要注意相关接口的使用。
(unordered_map与map的选择依据:题目仅需 “增删改查 + 统计大小”,无需有序性,unordered_map的哈希表实现(O (1) 查询)比map的红黑树实现(O (logn) 查询)更高效,)
代码
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;int main()
{//操作次数,操作数,分数int n, op, sc;cin >> n;unordered_map<string, int> m;string name;while (n--){cin >> op;if (op == 4){cout << m.size() << endl;}else if (op == 1){cin >> name >> sc;//插入+修改m[name] = sc;cout << "OK" << endl;}else if (op == 2){cin >> name;if (m.count(name)){cout << m[name] << endl;}else{cout << "Not found" << endl;}}else{cin >> name;if (m.count(name)){m.erase(name);cout << "Deleted successfully" << endl;}else{cout << "Not found" << endl;}}}return 0;
}
不重复数字
题目描述
题目解析
本题思路很简单,创建一个unordered_set,读取一个数据先判断它在不在unordered_set中,如果不在则把它插入unordered_set并输出。
但是本题需要注意可能超时,cout 本身带有缓冲,但 “多次小批量输出” 的开销远大于 “一次大批量输出”。所以我们可以先把数据存入数组中,再一次性输出,或者用scanf、printf。
(unordered_set的选择逻辑:题目仅需 “去重” 无需有序,unordered_set(O (1) 插入 / 查询)比set(O (logn))更优)
代码
//1、用scanf、printf替换cin、cout
#include <vector>
#include <unordered_set>
#include <stdio.h>
using namespace std;int main()
{//组数、每组数据数、元素数据int T, n, a;//cin >> T;scanf("%d", &T);//输入while (T--){unordered_set<int> s;//cin >> n;scanf("%d", &n);while (n--){//cin >> a;scanf("%d", &a);if (s.count(a)){//已有数据,直接continuecontinue;}printf("%d ", a);s.insert(a);}//cout << endl;printf("\n");}return 0;
}
//2、批量输出
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;int main()
{//组数、每组数据数、元素数据int T, n, a;cin >> T;unordered_set<int> s;vector<int> retv;//输入while (T--){//每组数据输入前先清空set、vectors.clear();retv.clear();cin >> n;while (n--){cin >> a;if (s.count(a)){//已有数据,直接continuecontinue;}retv.push_back(a);s.insert(a);}//输出for (auto e : retv){cout << e << " ";}cout << endl;}return 0;
}
阅读理解
题目描述
题目解析
本题需要活用我们学习过的容器,用一个unordered_map<string, set< int >>可以很高效地解决本题,首先unordered_map存储单词和在哪些文章中出现过的键值对,单词和在哪些文章中出现过用set存储,set自带去重和排升序的功能(unordered_set无排升序功能故选用set),所以最后直接范围for输出查找单词所对于的set就行了。
代码
#include <iostream>
#include <unordered_map>
#include <string>
#include <set>
using namespace std;int main()
{//读取短文int N;cin >> N;unordered_map<string, set<int>> m;for(int i = 1; i <= N; i++){int L;cin >> L;while (L--){string word;cin >> word;//m[word]返回set类型变量,i表示第几篇文章m[word].insert(i);}}//查询每篇短文生词出现数目int M;cin >> M;while (M--){string fword;cin >> fword;//查询某个单词在哪些文章中出现过//m[fword]中的元素自动升序排序,所以直接范围for遍历整个m[fword]就行了for (auto e : m[fword]){cout << e << " ";}cout << endl;}return 0;
}
A-B数对
题目描述
题目解析
本题思路是把枚举转化成查找。题目要求是满足A - B = C的数对个数,转化一下相当于求满足A = C + B的数对个数。
思路是首先遍历全部数据统计出每个数出现的次数,用unorder_map存储<数,每个数出现的次数>。然后再遍历全部数据,把遍历的每一个数当作B,unorder_map中的C + B的值出现的次数就是题目要求是A-B数对的个数(比如题目示例B为2,C为1,C + B = 3,3这个数出现过一次(在unorder_map中存在,且value为1),所以数对个数加1),unorder_map[C + B]的返回值就是每一个C + B的值出现的次数。
本题返回值ret需要用long long存储,因为极端情况比如:
假设数组所有元素都相同(比如全是 5),且C = 0。
此时对每个 B(共 2e5 个)来说,A = 0 + 5,而 A 的出现次数是 2e5 次(因为所有元素都是 5)。
总对数 = 每个 B 对应的 A 次数之和 = 2e5 * 2e5 = 4e10 超过了int的上限2e9。
代码
#include <iostream>
#include <unordered_map>
using namespace std;typedef long long LL;
const int N = 2e5 + 10;
int a[N]; //用于存储每一个输入的数值int main()
{int n, c;cin >> n >> c;// <数,每个数出现的次数>unordered_map<int, int> mp;//统计每个数出现的次数for (int i = 1; i <= n; i++){cin >> a[i];mp[a[i]]++;}// A = C + B,B = a[i]// 遍历全部数据作为B,统计C + a[i]出现的次数LL ret = 0;for (int i = 1; i <= n; i++){ret += mp[c + a[i]];}cout << ret << endl;return 0;
}
CitiesandStatesS
题目描述
题目解析
本题思路是找反向对应关系,当出现ab对应xy的数据时,需要找历史xy对应ab出现过的次数,每来一个数据都把统计的该次数加上,次数总和就是所有满足题目要求的特殊城市对数。这种对应关系我们用字符串拼接的方式来实现,把abxy拼成一个字符串存入unordered_map的key,value记录该字符串出现的次数。截取字符串用 substr实现,拼接字符串用string的operator+实现。
substr是截取子串接口,从pos位置开始截取len个字符。
本题还有一个条件:特殊城市需要来自不同的州,所以对于形如 ab_ _ _ -> ab 这样的数据不能统计,所以遇到城市名前两个字符和州名相同的数据时直接跳过,不参与统计。
代码
#include <iostream>
#include <string>
#include <unordered_map>using namespace std;int main()
{int n;cin >> n;unordered_map<string, int> mp;int ret = 0;while (n--){string a, b;cin >> a >> b;//<城市前两个字符拼接州名, 出现次数>//截取城市名前两个字符a = a.substr(0, 2);if (a == b){//城市前两个字符和城市所在州名相同//不符合题目规定“来自不同的州”直接continuecontinue;}//一对统计一次,只有在一对符合要求的第二个出现的数据才统计//先查后存,避免当前数据与自身匹配ret += mp[b + a];mp[a + b]++;}cout << ret << endl;return 0;
}
以上就是小编分享的全部内容了,如果觉得不错还请留下免费的赞和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~