题解 | #小红书推荐系统#
小红书推荐系统
https://www.nowcoder.com/practice/e5b39c9034a84bf2a5e026b2b9b973d0
题目链接
题目描述
根据小红的搜索记录(一个由空格分隔的单词组成的字符串),找出所有的“关键词”。一个单词被称为“关键词”,当且仅当它在搜索记录中出现的次数不少于3次。
请输出所有关键词,并遵循以下排序规则:
- 按出现频次从高到低排序。
- 如果频次相同,则按字典序升序排序。
解题思路
这是一个典型的词频统计和自定义排序问题。我们可以分步解决:
1. 词频统计
首先,我们需要知道每个单词在输入字符串中出现了多少次。最适合这个任务的数据结构是哈希表(在C++中是 map
,Java中是 HashMap
,Python中是 dict
)。
- 我们先将输入的整行字符串按空格分割成一个个独立的单词。
- 然后,遍历这些单词。对于每个单词,我们将其作为键(key)存入哈希表,并将其出现次数作为值(value)。如果单词已存在于哈希表中,就将其计数值加1;如果不存在,就插入这个新单词,并将计数值设为1。
2. 关键词筛选
完成词频统计后,哈希表中存储了所有单词及其出现次数。根据题目定义,我们需要筛选出出现次数不少于3次的单词。我们可以遍历哈希表,将满足 count >= 3
的键值对(单词和它的频次)提取出来,存入一个新的列表或数组中,以备后续排序。
3. 自定义排序 这是本题的核心。我们需要对筛选出的关键词列表进行排序。排序规则是双重的:
- 主排序键:单词的频次,要求降序排列。
- 次排序键:单词本身(字符串),要求升序(字典序)排列。
在实现时,我们可以提供一个自定义的比较函数或比较器。对于任意两个关键词 A 和 B:
- 如果 A 的频次不等于 B 的频次,那么频次高的排在前面。
- 如果 A 的频次等于 B 的频次,那么字典序小的排在前面。
4. 输出结果 排序完成后,我们只需遍历排序好的列表,依次输出每个关键词即可。
代码
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <algorithm>
using namespace std;
// 自定义排序的比较函数
bool compare(const pair<string, int>& a, const pair<string, int>& b) {
if (a.second != b.second) {
return a.second > b.second; // 按频次降序
}
return a.first < b.first; // 频次相同,按字典序升序
}
int main() {
string line;
getline(cin, line);
stringstream ss(line);
string word;
// 1. 词频统计
map<string, int> counts;
while (ss >> word) {
counts[word]++;
}
// 2. 关键词筛选
vector<pair<string, int>> keywords;
for (auto const& [key, val] : counts) {
if (val >= 3) {
keywords.push_back({key, val});
}
}
// 3. 自定义排序
sort(keywords.begin(), keywords.end(), compare);
// 4. 输出结果
for (const auto& p : keywords) {
cout << p.first << '\n';
}
return 0;
}
import java.util.Scanner;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
String[] words = line.split("\\s+");
// 1. 词频统计
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
counts.put(word, counts.getOrDefault(word, 0) + 1);
}
// 2. 关键词筛选
List<Map.Entry<String, Integer>> keywords = new ArrayList<>();
for (Map.Entry<String, Integer> entry : counts.entrySet()) {
if (entry.getValue() >= 3) {
keywords.add(entry);
}
}
// 3. 自定义排序
Collections.sort(keywords, (a, b) -> {
if (!a.getValue().equals(b.getValue())) {
return b.getValue().compareTo(a.getValue()); // 按频次降序
}
return a.getKey().compareTo(b.getKey()); // 频次相同,按字典序升序
});
// 4. 输出结果
for (Map.Entry<String, Integer> entry : keywords) {
System.out.println(entry.getKey());
}
}
}
import sys
from collections import Counter
def solve():
# 读取和分割输入
words = sys.stdin.readline().strip().split()
# 1. 词频统计
counts = Counter(words)
# 2. 关键词筛选
keywords = []
for word, count in counts.items():
if count >= 3:
keywords.append((word, count))
# 3. 自定义排序
# Python的sort/sorted可以接受一个元组作为key,依次比较元组中的元素
# 我们让频次取负数,即可实现降序排序
keywords.sort(key=lambda x: (-x[1], x[0]))
# 4. 输出结果
for word, count in keywords:
print(word)
solve()
算法及复杂度
- 算法:哈希表 + 自定义排序
- 时间复杂度:设输入字符串的总长度为
,单词数量为
,独立单词数量为
。
- 词频统计:
,因为需要遍历所有单词。
- 筛选和排序:
,这是对独立单词进行排序的开销,通常是性能瓶颈。
- 总时间复杂度为
。
- 词频统计:
- 空间复杂度:需要一个哈希表和一个列表来存储独立单词及其频次,因此空间复杂度为
,其中
是平均单词长度。最坏情况下,空间复杂度接近
。