逐篇文章累加单词总次数,并用文章编号标记同一单词在当前文章中是否已经出现。
OJ: shumeng
题目 ID: CSP202403A
难度:入门
标签:模拟计数
日期: 2026-07-31 16:21
形式化题目
有
对每个单词
- 单词
出现在了多少篇文章中(同一篇文章内多次出现只算一篇); - 单词
在所有文章中出现的总次数。
思路
这是两个不同的统计量,需要分别维护:总次数每次读到单词就累加;文章数只在单词于当前文章中第一次出现时累加。
朴素做法:每篇文章用集合去重
先看一个容易理解的想法:每篇文章用一个集合记录本次出现过的单词,文章读完后把集合里的单词文章数各加一。
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-07-31 16:21
* update_at: 2026-08-17 22:39
*/
// brute.cpp:小数据暴力解,用集合记录单篇文章出现过的单词,避免同一篇文章重复统计。
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 100005;
int n, m;
int article_count[MAXM]; // article_count[word] 表示单词 word 出现在多少篇文章中
int total_count[MAXM]; // total_count[word] 表示单词 word 在所有文章中出现的总次数
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int article = 1; article <= n; article++) {
int length;
cin >> length;
set<int> appeared; // 当前这篇文章出现过的单词,set 保证同一单词只记录一次
for (int j = 0; j < length; j++) {
int word;
cin >> word;
total_count[word]++;
appeared.insert(word);
}
// 文章读完后再把每个出现过的单词文章数加一
for (set<int>::iterator it = appeared.begin(); it != appeared.end(); ++it) {
article_count[*it]++;
}
}
for (int word = 1; word <= m; word++) {
cout << article_count[word] << ' ' << total_count[word] << '\n';
}
return 0;
}这个做法正确,但每篇文章都要维护一个集合,多了一层
优化:用文章编号代替集合去重
既然文章是按 last_article[word] 记录单词 word 最近一次累加文章数时的文章编号:
- 读到单词
word,先把total_count[word]加一; - 若
last_article[word] != 当前文章编号,说明这是当前文章中第一次出现,last_article[word]更新为当前编号,并把article_count[word]加一。
这样不需要任何集合结构,也不需要为每篇文章清空数组,扫描一遍即可完成两个统计量。
代码
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-07-31 16:21
* update_at: 2026-08-17 22:39
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 100005;
int n, m;
int article_count[MAXM]; // article_count[word] 表示单词 word 出现在多少篇文章中
int total_count[MAXM]; // total_count[word] 表示单词 word 在所有文章中出现的总次数
int last_article[MAXM]; // last_article[word] 表示单词 word 最近一次统计文章数的文章编号
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int article = 1; article <= n; article++) {
int length;
cin >> length;
for (int j = 0; j < length; j++) {
int word;
cin >> word;
total_count[word]++;
// 同一个单词在同一篇文章中只统计一次文章数
if (last_article[word] != article) {
last_article[word] = article;
article_count[word]++;
}
}
}
for (int word = 1; word <= m; word++) {
cout << article_count[word] << ' ' << total_count[word] << '\n';
}
return 0;
}复杂度
设所有文章中的单词总数为
- 时间:每读到一个单词做
的累加与判断,总时间复杂度 ;朴素集合做法为 。 - 空间:三个大小为
的数组,空间复杂度 。
总结
“出现总次数”与“出现文章数”是两个不同的统计量:前者每次出现都累加,后者同一篇文章内只能累加一次。用文章编号记录上次统计位置,就能在不引入集合的情况下完成去重,这是常见的“按顺序扫描 + 编号标记”技巧。