出现次数的TopK问题
题目描述
给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印 出次数前k名的字符串。
[要求]
如果strArr长度为N,时间复杂度请达到 O ( N log K ) O(N \log K) O(NlogK)
输入描述:
第一行两个整数N, k。N表示数组大小
接下来N行,每行一个字符串
输出描述:
输出K行,每行有一个字符串和一个整数。
你需要按照出现出现次数由小到大输出,若出现次数相同时字符串字典序较小的优先输出
示例1
输入
4 2
1
2
3
4
输出
1 1
2 1
示例2
输入
4 2
1
1
2
3
输出
1 2
2 1
备注:
1
⩽
N
⩽
1
0
5
1 \leqslant N \leqslant 10^5
1⩽N⩽105
∑
字符个数
⩽
1
0
5
\sum \text{字符个数} \leqslant 10^5
∑字符个数⩽105
1 ⩽ k ⩽ 本质不同的字符串数 1 \leqslant k \leqslant \text{本质不同的字符串数} 1⩽k⩽本质不同的字符串数
保证输入的字符串仅含有大小写字母/数字
题解:
一般涉及到 TopK 问题,首先应该考虑用堆。
- 首先,使用哈希表统计每个字符串的出现次数,时间复杂度 O ( n ) O(n) O(n);
- 然后,将哈希表的前 k 个元素插入到小根堆中,接着遍历哈希表剩下的元素,将其插入到小根堆中,始终保持堆中元素个数为 k 个,时间复杂度 O ( n l o g k ) O(nlogk) O(nlogk);
- 最后,将堆中的元素排序,频率从小到大,时间复杂度 O ( k l o g k ) O(klogk) O(klogk)。
所以,最终的时间复杂度为 O ( n l o g k ) O(nlogk) O(nlogk)。
代码:
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;
typedef pair<string, int> PSI;
class MyCompare {
public:
bool operator()( const PSI& a, const PSI& b) const {
if ( a.second == b.second )
return a.first < b.first;
return a.second > b.second;
}
};
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
unordered_map<string, int> hash;
string str;
while ( n-- ) {
cin >> str;
hash[str] += 1;
}
priority_queue<PSI, vector<PSI>, MyCompare > heap;
for ( auto& it : hash ) {
if ( heap.size() != k ) heap.push( it );
else {
auto top = heap.top();
if ( top.second == it.second && top.first > it.first
|| top.second < it.second ) {
heap.pop();
heap.push( it );
}
}
}
vector<PSI> ans;
while ( heap.size() ) {
ans.push_back( heap.top() );
heap.pop();
}
for ( int i = k - 1; i >= 0; --i ) {
cout << ans[i].first << ' ' << ans[i].second << endl;
}
return 0;
}