这道题目来自蓝桥杯2018年第九届真题,编号2279。题目要求我们分析一组日志数据,找出在特定时间窗口内获得足够点赞数的"热帖"。具体来说:
给定N条日志记录,每条记录包含时间戳ts和帖子id id。我们需要找出所有在任意长度为D的时间段内获得至少K个点赞的帖子id。这个问题看似简单,但需要考虑时间窗口的滑动特性以及大规模数据的处理效率。
首先我们需要明确几个关键概念:
题目给出的代码采用了暴力解法,其核心思路是:
这种解法的时间复杂度是O(N^2),对于大规模数据显然不够高效。在实际编程竞赛中,通常N可以达到10^5量级,这样的复杂度会导致超时。
更高效的解法是使用滑动窗口技术。我们可以对每个帖子的时间戳序列进行排序,然后使用双指针维护一个滑动窗口:
cpp复制vector<int> findHotPosts(int N, int D, int K, vector<pair<int,int>>& logs) {
unordered_map<int, vector<int>> postMap; // id到时间戳列表的映射
vector<int> result;
// 按id分类存储时间戳
for(auto& log : logs) {
postMap[log.second].push_back(log.first);
}
// 处理每个帖子
for(auto& [id, tsList] : postMap) {
sort(tsList.begin(), tsList.end());
int left = 0;
for(int right = 0; right < tsList.size(); ++right) {
// 移动左指针,使窗口内时间差<D
while(tsList[right] - tsList[left] >= D) {
left++;
}
// 检查窗口内元素数量
if(right - left + 1 >= K) {
result.push_back(id);
break;
}
}
}
sort(result.begin(), result.end());
return result;
}
优化后的算法:
原题代码使用了固定大小的二维向量(100002),这在内存使用上不够灵活。我们可以改用更灵活的容器:
cpp复制unordered_map<int, vector<int>> postMap;
for(int i = 0; i < N; ++i) {
int ts, id;
cin >> ts >> id;
postMap[id].push_back(ts);
}
需要特别注意几种特殊情况:
题目要求按id升序输出结果,因此最后需要对结果排序:
cpp复制sort(result.begin(), result.end());
for(int id : result) {
cout << id << endl;
}
code复制7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
预期输出:1 10
code复制3 10 1
1 2
2 3
3 4
预期输出:2 3 4
code复制4 10 3
1 1
1 1
1 1
2 2
预期输出:1
可以生成随机数据测试算法效率:
常见错误是将时间窗口理解为固定起点,实际上需要检查所有可能的D长度窗口。例如错误代码:
cpp复制// 错误示例:只检查第一个时间戳开始的窗口
for(int i = 0; i < tsList.size(); ++i) {
if(tsList[i] - tsList[0] < D) {
count++;
}
}
正确的滑动窗口需要动态调整左右指针:
cpp复制int left = 0;
for(int right = 0; right < tsList.size(); ++right) {
while(tsList[right] - tsList[left] >= D) {
left++;
}
// 检查窗口大小
}
cpp复制ios::sync_with_stdio(false);
cin.tie(nullptr);
实际问题中可能需要考虑:
如果需要实时处理数据流,可以考虑:
对于超大规模数据,可以:
在实际编程竞赛中,这类问题考察的是对滑动窗口算法的理解和实现能力。建议通过大量练习掌握各种窗口滑动技巧,并注意边界条件的处理。对于蓝桥杯等竞赛,还需要特别注意输入输出格式和效率问题。