下面是2026年 GESP 5级 C++ 5级真题《找数》:
给定一个包含 $n$ 个互不相同的正整数的数组 $A$ 与一个包含 $m$ 个互不相同的正整数的数组 $B$,请你帮忙计算有多少个数在数组 $A$ 与数组 $B$ 中均出现。
数据范围
对于 $40%$ 的数据,保证 $1 \leq n,m \leq 1000$。 对于 $100%$ 的数据,保证 $1 \leq n,m \leq 10^5$,$1 \leq a_i,b_i \leq 10^9$。
输入说明
第一行包含两个整数 $n,m$。 第二行包含 $n$ 个正整数 $a_1,a_2,\cdots,a_n$ 表示数组 $A$。 第三行包含 $m$ 个正整数 $b_1,b_2,\cdots,b_m$ 表示数组 $B$。
输出说明
输出一个整数,表示在数组 $A$ 与数组 $B$ 中均出现的数的个数。
输入样例
3 5
4 2 3
3 1 5 4 6
输出样例
2
题目想让我们做什么?
你可以把这道题想象成:
- 小明有一盒彩色卡片
A - 小红有一盒彩色卡片
B - 现在想知道:他们俩都有哪几张卡片,一共有几张
题目不是让你把这些共同的数都输出出来,而是只输出“个数”。
最容易想到的方法:一个一个比
比如:
- 先拿
A的第一个数,去B里全部找一遍 - 再拿
A的第二个数,去B里全部找一遍 - 一直这样比较下去
这个方法能做出来,但是如果数字很多,就会特别慢。 因为:
A最多有100000个数B最多有100000个数 如果每个都去逐个比较,最多要比较:100000 × 100000 = 10000000000这是非常大的数字,OJ 很可能会超时。
更聪明的方法:先做“快速查找表”
我们先把数组 A 中的数都放进一个“快速查找盒子”里。
然后读数组 B 时:
- 取出一个数
- 立刻查它在不在这个盒子里
- 如果在,就说明它在两个数组中都出现了
这个“快速查找盒子”就是 unordered_set。
举个例子
输入:
3 5
4 2 3
3 1 5 4 6
第一步:把 A 放进集合
A = [4, 2, 3]
集合里就有:
{4, 2, 3}
第二步:检查 B
B = [3, 1, 5, 4, 6]
依次看:
3在集合里,答案加 11不在5不在4在集合里,答案加 16不在
最后答案就是 2。
为什么题目特别说“互不相同”?
题目说:
- 数组
A中的数互不相同 - 数组
B中的数互不相同
这意味着:
A里不会有重复数字B里也不会有重复数字
这样我们统计起来就简单了。
如果 B 里一个数字重复出现很多次,那就要额外考虑“算几次”的问题。
但这道题已经帮我们排除了这个麻烦。
五、时间复杂度
这份做法的效率
- 把
A放入集合:O(n) - 枚举
B并查找:O(m)
总复杂度大约是:
O(n + m)
这个速度很快,可以通过 10^5 的数据范围。
六、容易出错的地方
1. 用双重循环暴力比较
像这样:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i] == b[j]) ans++;
}
}
虽然逻辑上没错,但数据大时会超时。
2. 忘记这题只要输出“个数”
有些同学会把共同的数字打印出来,但题目要求的是:
输出共同出现的数的个数
所以最后只输出一个整数。
3. 把“出现”理解错
题目问的是:
在
A和B中都出现的数有多少个
不是求和,不是找最大值,也不是找相同位置。 只要一个数字在两个数组里都出现,就算一个。
参考程序
下面是一个参考实现:
#include <iostream>
#include <unordered_set>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
unordered_set<int> aSet;
int num;
// 读入数组 A,并保存到集合中
for (int i = 0; i < n; i++) {
cin >> num;
aSet.insert(num);
}
int count = 0;
// 读入数组 B
// 如果这个数也在 A 中出现过,就计数加一
for (int i = 0; i < m; i++) {
cin >> num;
if (aSet.count(num)) {
count++;
}
}
cout << count << '\n';
return 0;
}
代码讲解
1. 为什么用 unordered_set
unordered_set 可以理解成一个“查找特别快的集合”。
它有两个很重要的特点:
- 可以存很多数
- 判断一个数在不在里面,速度很快 这题正好需要频繁判断:
“B 里的这个数,在不在 A 里?”
所以特别适合用它。
2. 代码在做什么
第一步:把 A 里的所有数存起来
unordered_set<int> s;
for (int i = 0; i < n; i++) {
cin >> x;
s.insert(x);
}
比如 A = [4, 2, 3],那么集合里就有:
{4, 2, 3}
第二步:枚举 B,看看每个数是不是也在 A 里
int ans = 0;
for (int i = 0; i < m; i++) {
cin >> x;
if (s.count(x)) {
ans++;
}
}
如果 B 中某个数也在集合 s 里,就说明它同时出现在 A 和 B 中,答案加一。
总结
这道题的核心就是:
先把一个数组放进哈希集合,再去检查另一个数组里的每个数是否存在。
这是算法题里非常常见的一类题,属于:
- 哈希
- 集合查找
- 统计交集元素个数