不止于算法:用CCF CSP词频统计题,聊聊数据处理中的‘集合’与‘计数’思维
在数据分析与后端开发中,统计特定ID在不同时间段的出现情况是高频需求。无论是用户行为日志分析、商品点击统计,还是系统监控指标聚合,本质上都在处理同一类问题:如何高效地对分组数据进行去重计数和总量统计。CCF CSP认证中的词频统计题,恰好为我们提供了一个绝佳的微型案例,来探讨这类问题的通用解法。
这道题看似简单,却蕴含了数据处理的核心思维——集合运算与计数聚合。我们将跳出"解题"框架,将其视为一个完整的数据处理管道:输入是分组序列,输出是两个维度的聚合指标。通过对比不同实现方案的时空复杂度,不仅能提升算法能力,更能培养将竞赛思维迁移到真实业务场景的能力。
1. 问题抽象与业务场景映射
词频统计题的核心是计算两个指标:
- 文章覆盖数(xi):单词出现在多少篇不同的文章中(按篇去重)
- 总出现次数(yi):单词在所有文章中出现的总次数
这直接对应着业务中的两类常见需求:
- UV统计:比如统计某个商品被多少独立用户浏览过
- PV统计:比如统计某个API接口被调用的总次数
假设我们有以下电商日志数据:
plaintext复制用户1 浏览记录: [商品A, 商品B, 商品A]
用户2 浏览记录: [商品B, 商品C]
用户3 浏览记录: [商品A, 商品C]
对应的统计结果应该是:
| 商品 | 浏览用户数(UV) | 浏览次数(PV) |
|---|---|---|
| A | 2 | 3 |
| B | 2 | 2 |
| C | 2 | 2 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据结构选型与实现对比
2.1 基础实现:数组+布尔标记
原题参考解法使用了bool appeared[m]数组来标记单词是否在当前文章中出现过。这种方案的优点是:
- 空间效率高:仅需O(m)的额外空间
- 访问速度快:数组随机访问时间复杂度O(1)
cpp复制bool appeared[m] = {false};
for (int j = 0; j < count; j++) {
int num = /* 读取单词 */;
if (!appeared[num - 1]) {
appeared[num - 1] = true;
result[num - 1][0]++; // 文章数+1
}
result[num - 1][1]++; // 总次数+1
}
但在实际业务中,这种方案存在局限性:
- 需要提前知道数据范围(m)
- 当ID是非连续数值或字符串时无法直接应用
2.2 进阶实现:哈希表+集合
更通用的做法是使用哈希表记录每个单词的统计结果,并用集合来去重:
python复制from collections import defaultdict
def word_statistics(articles):
stats = defaultdict(lamb
