The Search Suggestions System problem requires you to build an autocomplete system that suggests up to three product names from a list of products
after each character of a searchWord
is typed. The suggestions must be products that start with the current prefix and must be sorted lexicographically. If there are more than three matching products, return only the first three in lex order. For each prefix of the searchWord
(i.e., after typing each character), you return a list of up to three product suggestions.
Constraints:
products
is a string of lowercase English letters.products
contains no duplicates.
When first approaching this problem, one might consider checking every product for every prefix of the searchWord
. For each character typed, filter the list of products to those that start with the current prefix and sort them to get the top three. However, this approach is inefficient for large lists, as it repeats much of the same work for each new character.
To optimize, we notice that:
Here’s a step-by-step breakdown of the efficient solution:
products
lexicographically. This ensures that any prefix search will yield suggestions in the correct order.searchWord
:
searchWord
.products
.Why this works: By sorting once and using binary search, we minimize repeated work and always present the lexicographically smallest suggestions. This approach is efficient and easy to implement.
Let's walk through an example:
Input:
products = ["mobile","mouse","moneypot","monitor","mousepad"]
searchWord = "mouse"
["mobile","moneypot","monitor","mouse","mousepad"]
["mobile","moneypot","monitor"]
["mobile","moneypot","monitor"]
["mouse","mousepad"]
["mouse","mousepad"]
["mouse","mousepad"]
Output:
[["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]]
Brute-force approach:
searchWord
.The optimized approach is much more efficient, especially for large lists and long search words.
from bisect import bisect_left
class Solution:
def suggestedProducts(self, products, searchWord):
products.sort()
res = []
prefix = ""
for c in searchWord:
prefix += c
# Find leftmost index where prefix could be inserted
i = bisect_left(products, prefix)
suggestions = []
# Check up to three products starting at index i
for j in range(i, min(i+3, len(products))):
if products[j].startswith(prefix):
suggestions.append(products[j])
else:
break
res.append(suggestions)
return res
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<string>> suggestedProducts(vector<string>& products, string searchWord) {
sort(products.begin(), products.end());
vector<vector<string>> res;
string prefix = "";
for (char c : searchWord) {
prefix += c;
auto it = lower_bound(products.begin(), products.end(), prefix);
vector<string> suggestions;
for (int i = 0; i < 3 && it + i != products.end(); ++i) {
if ((it + i)->substr(0, prefix.size()) == prefix)
suggestions.push_back(*(it + i));
else
break;
}
res.push_back(suggestions);
}
return res;
}
};
import java.util.*;
class Solution {
public List<List<String>> suggestedProducts(String[] products, String searchWord) {
Arrays.sort(products);
List<List<String>> res = new ArrayList<>();
String prefix = "";
for (char c : searchWord.toCharArray()) {
prefix += c;
int i = Arrays.binarySearch(products, prefix);
if (i < 0) i = -(i + 1);
List<String> suggestions = new ArrayList<>();
for (int j = i; j < Math.min(i + 3, products.length); ++j) {
if (products[j].startsWith(prefix)) {
suggestions.add(products[j]);
} else {
break;
}
}
res.add(suggestions);
}
return res;
}
}
/**
* @param {string[]} products
* @param {string} searchWord
* @return {string[][]}
*/
var suggestedProducts = function(products, searchWord) {
products.sort();
let res = [];
let prefix = "";
for (let i = 0; i < searchWord.length; ++i) {
prefix += searchWord[i];
// Binary search to find leftmost index
let l = 0, r = products.length;
while (l < r) {
let m = Math.floor((l + r) / 2);
if (products[m] < prefix) l = m + 1;
else r = m;
}
let suggestions = [];
for (let j = l; j < Math.min(l + 3, products.length); ++j) {
if (products[j].startsWith(prefix)) {
suggestions.push(products[j]);
} else {
break;
}
}
res.push(suggestions);
}
return res;
};
The Search Suggestions System problem is elegantly solved by sorting the product list and using binary search to efficiently find suggestions for each prefix of the search word. This approach avoids redundant work, guarantees lexicographical order, and is simple to implement. The key insight is that as the prefix grows, the set of matching products only shrinks, allowing us to efficiently narrow our search and always provide the best possible suggestions.