You are given an array of strings products and a string searchWord.
Design a system that suggests at most three product names from products after each character of searchWord is typed. Suggested products should have common prefix with searchWord. If there are more than three products with a common prefix return the three lexicographically minimums products.
Return a list of lists of the suggested products after each character of searchWord is typed.
Example 1:
Input: products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
Output: [["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]]
Explanation: products sorted lexicographically = ["mobile","moneypot","monitor","mouse","mousepad"].
After typing m and mo all products match and we show user ["mobile","moneypot","monitor"].
After typing mou, mous and mouse the system suggests ["mouse","mousepad"].
Example 2:
Input: products = ["havana"], searchWord = "havana"
Output: [["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]
Explanation: The only word "havana" will be always suggested while typing the search word.
Constraints:
1 <= products.length <= 1000
1 <= products[i].length <= 3000
1 <= sum(products[i].length) <= 2 * 10^4
All the strings of products are unique.
products[i] consists of lowercase English letters.
1 <= searchWord.length <= 1000
searchWord consists of lowercase English letters.
Solved after help.
For searching top frequent words, it’s implemented by using a heap to store all the possible words for each node. Since we need to return all the smallest words, we use a max-heap to store all the candidates.
How to build a max-heap for strings?
Use a self defined class like below:
class MaxHeapObj(str):
def __init__(self, string):
self.string = string
def __lt__(self, other):
return self.string > other.string
def __eq__(self, other):
return self.string == other.string
MaxHeapObj
is a sub-class of str
, and we changed the rule of <
.
For this question, if the word doesn’t match anything in the TrieTree, then we return an empty list for suggestions.
Sort the product, and use sub strings from searchWord
to search. All the products that are at the right of insert_index
are possible suggestions, because they are “larger” than the sub-string, so they are either longer, or have same length but have “larger” alphabets. And we don’t want the latter one.
Time complexity: o ( n log n ) o(n \log n) o(nlogn)
Space complexity: o ( n ) o(n) o(n)
class MaxHeapObj(str):
def __init__(self, string):
self.string = string
def __lt__(self, other):
return self.string > other.string
def __eq__(self, other):
return self.string == other.string
class TrieNode:
def __init__(self):
self.children = {}
self.suggestions = []
self.is_end = False
def add_suggestion(self, word: str) -> None:
if len(self.suggestions) == 3:
heapq.heappushpop(self.suggestions, MaxHeapObj(word))
else:
heapq.heappush(self.suggestions, MaxHeapObj(word))
def get_suggestions(self):
return sorted(self.suggestions, reverse=True)
class TrieTree:
def __init__(self):
self.root = TrieNode()
def insert(self, word: str) -> None:
node = self.root
for each_char in word:
if each_char not in node.children:
node.children[each_char] = TrieNode()
node = node.children[each_char]
node.add_suggestion(word)
node.is_end = True
def get_suggestions(self, word: str) -> list:
node = self.root
i = 0
while i < len(word):
if word[i] not in node.children:
break
node = node.children[word[i]]
i += 1
return node.get_suggestions() if i == len(word) else []
class Solution:
def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]:
products.sort()
trie_tree = TrieTree()
for each_p in products:
trie_tree.insert(each_p)
res = []
for i in range(len(searchWord)):
res.append(trie_tree.get_suggestions(searchWord[:i + 1]))
return res
class Solution:
def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]:
products.sort()
res = []
for i in range(len(searchWord)):
cur_word = searchWord[:i + 1]
insert_index = bisect.bisect_left(products, cur_word)
cur_res = []
for next_i in range(insert_index, len(products)):
if len(cur_res) == 3:
break
if products[next_i].startswith(cur_word):
cur_res.append(products[next_i])
res.append(cur_res.copy())
return res