Medium
题目描述
给你一个产品数组 products 和一个字符串 searchWord。
设计一个推荐系统,在输入 searchWord 的每个字符后,推荐最多三个产品名称。推荐的产品应与 searchWord 有共同前缀。如果有超过三个产品有共同前缀,返回按字典序最小的三个产品。
返回在输入 searchWord 每个字符后建议的产品列表的列表。
示例 1:
输入:products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
输出:[["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]]
解释:按字典序排序后 products = ["mobile","moneypot","monitor","mouse","mousepad"]。
输入 m 和 mo 后,所有产品都匹配,显示 ["mobile","moneypot","monitor"]。
输入 mou、mous 和 mouse 后,系统推荐 ["mouse","mousepad"]。
示例 2:
输入:products = ["havana"], searchWord = "havana"
输出:[["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]
解释:唯一的单词 "havana" 在输入搜索词时总是被建议。
提示:
1 <= products.length <= 10001 <= products[i].length <= 30001 <= sum(products[i].length) <= 2 * 10^4products的所有字符串都是唯一的products[i]由小写英文字母组成1 <= searchWord.length <= 1000searchWord由小写英文字母组成
解题思路
这个问题有多种解法:
解法1:排序+双指针(推荐)
最直观的方法是先将产品数组排序,这样符合前缀的产品会连续出现。对于每个前缀,我们可以用双指针找到匹配范围,然后取前三个。
解法2:字典树(Trie)
构建字典树存储所有产品,在每个节点保存按字典序排列的前三个产品。查询时沿着字典树向下遍历即可。
解法3:二分查找
在排序后的数组中,用二分查找找到第一个以当前前缀开头的产品位置,然后取后续最多三个产品。
考虑到题目约束和实现复杂度,排序+双指针是最平衡的方案,代码简洁且效率高。
算法步骤:
- 对产品数组排序,确保字典序
- 对于每个前缀长度,用双指针维护匹配区间
- 在区间内取前三个产品作为推荐结果
代码实现
class Solution {
public:
vector<vector<string>> suggestedProducts(vector<string>& products, string searchWord) {
sort(products.begin(), products.end());
vector<vector<string>> result;
int left = 0, right = products.size() - 1;
for (int i = 0; i < searchWord.length(); i++) {
char c = searchWord[i];
// 缩小左边界
while (left <= right && (products[left].length() <= i || products[left][i] != c)) {
left++;
}
// 缩小右边界
while (left <= right && (products[right].length() <= i || products[right][i] != c)) {
right--;
}
// 收集最多3个结果
vector<string> suggestions;
for (int j = left; j <= right && j < left + 3; j++) {
suggestions.push_back(products[j]);
}
result.push_back(suggestions);
}
return result;
}
};
class Solution:
def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]:
products.sort()
result = []
left, right = 0, len(products) - 1
for i in range(len(searchWord)):
c = searchWord[i]
# 缩小左边界
while left <= right and (len(products[left]) <= i or products[left][i] != c):
left += 1
# 缩小右边界
while left <= right and (len(products[right]) <= i or products[right][i] != c):
right -= 1
# 收集最多3个结果
suggestions = []
for j in range(left, min(right + 1, left + 3)):
suggestions.append(products[j])
result.append(suggestions)
return result
public class Solution {
public IList<IList<string>> SuggestedProducts(string[] products, string searchWord) {
Array.Sort(products);
var result = new List<IList<string>>();
int left = 0, right = products.Length - 1;
for (int i = 0; i < searchWord.Length; i++) {
char c = searchWord[i];
// 缩小左边界
while (left <= right && (products[left].Length <= i || products[left][i] != c)) {
left++;
}
// 缩小右边界
while (left <= right && (products[right].Length <= i || products[right][i] != c)) {
right--;
}
// 收集最多3个结果
var suggestions = new List<string>();
for (int j = left; j <= right && j < left + 3; j++) {
suggestions.Add(products[j]);
}
result.Add(suggestions);
}
return result;
}
}
var suggestedProducts = function(products, searchWord) {
products.sort();
const result = [];
let left = 0, right = products.length - 1;
for (let i = 0; i < searchWord.length; i++) {
const c = searchWord[i];
// 缩小左边界
while (left <= right && (products[left].length <= i || products[left][i] !== c)) {
left++;
}
// 缩小右边界
while (left <= right && (products[right].length <= i || products[right][i] !== c)) {
right--;
}
// 收集最多3个结果
const suggestions = [];
for (let j = left; j <= right && j < left + 3; j++) {
suggestions.push(products[j]);
}
result.push(suggestions);
}
return result;
};
复杂度分析
| 复杂度类型 | 大小 |
|---|---|
| 时间复杂度 | O(n log n + m·n) |
| 空间复杂度 | O(1) |
其中 n 是产品数量,m 是搜索词长度。排序需要 O(n log n),查询过程最坏情况下需要 O(m·n)。