分别用合法姓名集合和已点名集合区分 WRONG、OK 与 REPEAT。
OJ: luogu
题目 ID: P2580
难度:普及
标签:集合字符串状态记录python
创建: 2026-07-16 19:57
更新: 2026-10-07 12:15
题意
每次读到一个姓名:不在名单输出 WRONG,第一次点到合法姓名输出 OK,之后再次点到输出 REPEAT。
思路
维护两个集合:
valid:完整合法名单,始终不变;called:已经正确点到过的姓名。
按 name not in valid、name in called 的顺序判断即可。只有输出 OK 时才加入 called,错误姓名重复出现仍然应输出 WRONG。
Python 知识
- 集合推导式
{next(data) for ...}直接读取名单。 bytes可作为集合键,不必为每个姓名解码成 Unicode 字符串。set.add记录已出现状态,平均复杂度。 - 答案列表最后用换行连接,避免频繁输出。
代码
python
import sys
# 一次性读入全部输入,用迭代器依次取数
data = iter(sys.stdin.buffer.read().split())
# 读入 n 个姓名作为名单
n = int(next(data))
valid = {next(data) for _ in range(n)} # 名单集合,O(1) 查询
# 处理 m 次点名
m = int(next(data))
called = set() # 已点到过的姓名
answers = [] # 收集每次结果
for _ in range(m):
name = next(data)
if name not in valid:
answers.append("WRONG") # 不在名单中
elif name in called:
answers.append("REPEAT") # 已经点过了
else:
called.add(name)
answers.append("OK") # 第一次点到
print("\n".join(answers))STL 写法
用 map<string, int> 直接把姓名当键、状态当值:1 表示在名单里还没点过,2 表示已经点过。查询时用 find,返回 end() 就说明名字不在名单,输出 WRONG;命中且值为 1 时改成 2 并输出 OK;命中且值为 2 时输出 REPEAT。这里刻意不写 state[name]:下标访问会把没见过的姓名插进表里,默认值 0 反而把“不在名单”和“在名单未点名”混在一起。相比上面手写的 Trie,省掉了 26 叉的 trie 数组和 node_cnt 维护,代价是每次查询多
对应的 cppbook 章节:map:通过键找到对应的值
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-07 01:15
* update_at: 2026-10-07 01:20
*/
// 这是 STL 写法:用 map<string, int> 建立“姓名 -> 状态”的对照表。
// 状态含义:1 表示在名单里且还没被点到,2 表示已经被点到过;
// 查询用 find,不用 state[name],因为下标访问会把没见过的姓名插进表里,
// 那样就分不清“不在名单”和“在名单但没点过”。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<string, int> state; // state[name] 表示姓名 name 的状态:1 未点名,2 已点名
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll n;
cin >> n;
for (ll i = 1; i <= n; i++) {
string name;
cin >> name;
state[name] = 1; // 名单里的姓名统一记成“未点名”
}
ll m;
cin >> m;
for (ll i = 1; i <= m; i++) {
string name;
cin >> name;
// find 只查询、不插入:返回 end() 就说明这个名字根本不在名单里
auto it = state.find(name);
if (it == state.end()) {
cout << "WRONG\n";
} else if (it->second == 1) {
it->second = 2; // 第一次点到合法姓名,标记成已点名
cout << "OK\n";
} else {
cout << "REPEAT\n";
}
}
return 0;
}复杂度
设姓名总字符数为
总结
C++ 常把本题作为 Trie 模板;Python 中完整字符串集合就是更自然的等价数据结构。