|
| 1 | +--- |
| 2 | +comments: true |
| 3 | +difficulty: Medium |
| 4 | +edit_url: Antim |
| 5 | +rating: 1573 |
| 6 | +source: Weekly Contest 164 Q3 |
| 7 | +tags: |
| 8 | + - Trie |
| 9 | + - Array |
| 10 | + - String |
| 11 | + - Binary Search |
| 12 | + - Sorting |
| 13 | + - Heap (Priority Queue) |
| 14 | +--- |
| 15 | + |
| 16 | +<!-- problem:start --> |
| 17 | + |
| 18 | +# [1268. Search Suggestions System](https://leetcode.com/problems/search-suggestions-system) |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +## Description |
| 23 | + |
| 24 | +<!-- description:start --> |
| 25 | + |
| 26 | +<p>You are given an array of strings <code>products</code> and a string <code>searchWord</code>.</p> |
| 27 | + |
| 28 | +<p>Design a system that suggests at most three product names from <code>products</code> after each character of <code>searchWord</code> is typed. Suggested products should have common prefix with <code>searchWord</code>. If there are more than three products with a common prefix return the three lexicographically minimums products.</p> |
| 29 | + |
| 30 | +<p>Return <em>a list of lists of the suggested products after each character of </em><code>searchWord</code><em> is typed</em>.</p> |
| 31 | + |
| 32 | +<p> </p> |
| 33 | +<p><strong class="example">Example 1:</strong></p> |
| 34 | + |
| 35 | +<pre> |
| 36 | +<strong>Input:</strong> products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse" |
| 37 | +<strong>Output:</strong> [["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]] |
| 38 | +<strong>Explanation:</strong> products sorted lexicographically = ["mobile","moneypot","monitor","mouse","mousepad"]. |
| 39 | +After typing m and mo all products match and we show user ["mobile","moneypot","monitor"]. |
| 40 | +After typing mou, mous and mouse the system suggests ["mouse","mousepad"]. |
| 41 | +</pre> |
| 42 | + |
| 43 | +<p><strong class="example">Example 2:</strong></p> |
| 44 | + |
| 45 | +<pre> |
| 46 | +<strong>Input:</strong> products = ["havana"], searchWord = "havana" |
| 47 | +<strong>Output:</strong> [["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]] |
| 48 | +<strong>Explanation:</strong> The only word "havana" will be always suggested while typing the search word. |
| 49 | +</pre> |
| 50 | + |
| 51 | +<p> </p> |
| 52 | +<p><strong>Constraints:</strong></p> |
| 53 | + |
| 54 | +<ul> |
| 55 | + <li><code>1 <= products.length <= 1000</code></li> |
| 56 | + <li><code>1 <= products[i].length <= 3000</code></li> |
| 57 | + <li><code>1 <= sum(products[i].length) <= 2 * 10<sup>4</sup></code></li> |
| 58 | + <li>All the strings of <code>products</code> are <strong>unique</strong>.</li> |
| 59 | + <li><code>products[i]</code> consists of lowercase English letters.</li> |
| 60 | + <li><code>1 <= searchWord.length <= 1000</code></li> |
| 61 | + <li><code>searchWord</code> consists of lowercase English letters.</li> |
| 62 | +</ul> |
| 63 | + |
| 64 | +<!-- description:end --> |
| 65 | + |
| 66 | +## Solutions |
| 67 | + |
| 68 | +<!-- solution:start --> |
| 69 | + |
| 70 | +### Solution 1: Sorting + Trie |
| 71 | + |
| 72 | +The problem requires that after each letter of the input `searchWord`, recommend up to three products from the `products` array that have the same prefix as `searchWord`. If there are more than three products with the same prefix that can be recommended, return the three with the smallest lexicographic order. |
| 73 | + |
| 74 | +To find products with the same prefix, we can use a trie; to return the three products with the smallest lexicographic order, we can first sort the `products` array, and then store the indices of the sorted array in the trie. |
| 75 | + |
| 76 | +Each node of the trie maintains the following information: |
| 77 | + |
| 78 | +- `children`: This is an array of length $26$, used to store the child nodes of the current node. `children[i]` represents the node whose character is `i + 'a'` among the child nodes of the current node. |
| 79 | +- `v`: This is an array, used to store the indices of the characters in the `products` array among the child nodes of the current node, storing up to three indices. |
| 80 | + |
| 81 | +During the search, we start from the root node of the trie, find the index array corresponding to each prefix, and store it in the result array. Finally, we only need to map each index in the index array to the `products` array. |
| 82 | + |
| 83 | +The time complexity is $O(L \times \log n + m)$, and the space complexity is $O(L)$. Where $L$ is the sum of the lengths of all strings in the `products` array, and $n$ and $m$ are the lengths of the `products` array and `searchWord`, respectively. |
| 84 | + |
| 85 | +<!-- tabs:start --> |
| 86 | + |
| 87 | +#### Python3 |
| 88 | + |
| 89 | +```python |
| 90 | +class Trie: |
| 91 | + def __init__(self): |
| 92 | + self.children: List[Union[Trie, None]] = [None] * 26 |
| 93 | + self.v: List[int] = [] |
| 94 | + |
| 95 | + def insert(self, w, i): |
| 96 | + node = self |
| 97 | + for c in w: |
| 98 | + idx = ord(c) - ord('a') |
| 99 | + if node.children[idx] is None: |
| 100 | + node.children[idx] = Trie() |
| 101 | + node = node.children[idx] |
| 102 | + if len(node.v) < 3: |
| 103 | + node.v.append(i) |
| 104 | + |
| 105 | + def search(self, w): |
| 106 | + node = self |
| 107 | + ans = [[] for _ in range(len(w))] |
| 108 | + for i, c in enumerate(w): |
| 109 | + idx = ord(c) - ord('a') |
| 110 | + if node.children[idx] is None: |
| 111 | + break |
| 112 | + node = node.children[idx] |
| 113 | + ans[i] = node.v |
| 114 | + return ans |
| 115 | + |
| 116 | + |
| 117 | +class Solution: |
| 118 | + def suggestedProducts( |
| 119 | + self, products: List[str], searchWord: str |
| 120 | + ) -> List[List[str]]: |
| 121 | + products.sort() |
| 122 | + trie = Trie() |
| 123 | + for i, w in enumerate(products): |
| 124 | + trie.insert(w, i) |
| 125 | + return [[products[i] for i in v] for v in trie.search(searchWord)] |
| 126 | +``` |
| 127 | + |
| 128 | +#### Java |
| 129 | + |
| 130 | +```java |
| 131 | +class Trie { |
| 132 | + Trie[] children = new Trie[26]; |
| 133 | + List<Integer> v = new ArrayList<>(); |
| 134 | + |
| 135 | + public void insert(String w, int i) { |
| 136 | + Trie node = this; |
| 137 | + for (int j = 0; j < w.length(); ++j) { |
| 138 | + int idx = w.charAt(j) - 'a'; |
| 139 | + if (node.children[idx] == null) { |
| 140 | + node.children[idx] = new Trie(); |
| 141 | + } |
| 142 | + node = node.children[idx]; |
| 143 | + if (node.v.size() < 3) { |
| 144 | + node.v.add(i); |
| 145 | + } |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + public List<Integer>[] search(String w) { |
| 150 | + Trie node = this; |
| 151 | + int n = w.length(); |
| 152 | + List<Integer>[] ans = new List[n]; |
| 153 | + Arrays.setAll(ans, k -> new ArrayList<>()); |
| 154 | + for (int i = 0; i < n; ++i) { |
| 155 | + int idx = w.charAt(i) - 'a'; |
| 156 | + if (node.children[idx] == null) { |
| 157 | + break; |
| 158 | + } |
| 159 | + node = node.children[idx]; |
| 160 | + ans[i] = node.v; |
| 161 | + } |
| 162 | + return ans; |
| 163 | + } |
| 164 | +} |
| 165 | + |
| 166 | +class Solution { |
| 167 | + public List<List<String>> suggestedProducts(String[] products, String searchWord) { |
| 168 | + Arrays.sort(products); |
| 169 | + Trie trie = new Trie(); |
| 170 | + for (int i = 0; i < products.length; ++i) { |
| 171 | + trie.insert(products[i], i); |
| 172 | + } |
| 173 | + List<List<String>> ans = new ArrayList<>(); |
| 174 | + for (var v : trie.search(searchWord)) { |
| 175 | + List<String> t = new ArrayList<>(); |
| 176 | + for (int i : v) { |
| 177 | + t.add(products[i]); |
| 178 | + } |
| 179 | + ans.add(t); |
| 180 | + } |
| 181 | + return ans; |
| 182 | + } |
| 183 | +} |
| 184 | +``` |
| 185 | + |
| 186 | +#### C++ |
| 187 | + |
| 188 | +```cpp |
| 189 | +class Trie { |
| 190 | +public: |
| 191 | + void insert(string& w, int i) { |
| 192 | + Trie* node = this; |
| 193 | + for (int j = 0; j < w.size(); ++j) { |
| 194 | + int idx = w[j] - 'a'; |
| 195 | + if (!node->children[idx]) { |
| 196 | + node->children[idx] = new Trie(); |
| 197 | + } |
| 198 | + node = node->children[idx]; |
| 199 | + if (node->v.size() < 3) { |
| 200 | + node->v.push_back(i); |
| 201 | + } |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + vector<vector<int>> search(string& w) { |
| 206 | + Trie* node = this; |
| 207 | + int n = w.size(); |
| 208 | + vector<vector<int>> ans(n); |
| 209 | + for (int i = 0; i < w.size(); ++i) { |
| 210 | + int idx = w[i] - 'a'; |
| 211 | + if (!node->children[idx]) { |
| 212 | + break; |
| 213 | + } |
| 214 | + node = node->children[idx]; |
| 215 | + ans[i] = move(node->v); |
| 216 | + } |
| 217 | + return ans; |
| 218 | + } |
| 219 | + |
| 220 | +private: |
| 221 | + vector<Trie*> children = vector<Trie*>(26); |
| 222 | + vector<int> v; |
| 223 | +}; |
| 224 | + |
| 225 | +class Solution { |
| 226 | +public: |
| 227 | + vector<vector<string>> suggestedProducts(vector<string>& products, string searchWord) { |
| 228 | + sort(products.begin(), products.end()); |
| 229 | + Trie* trie = new Trie(); |
| 230 | + for (int i = 0; i < products.size(); ++i) { |
| 231 | + trie->insert(products[i], i); |
| 232 | + } |
| 233 | + vector<vector<string>> ans; |
| 234 | + for (auto& v : trie->search(searchWord)) { |
| 235 | + vector<string> t; |
| 236 | + for (int i : v) { |
| 237 | + t.push_back(products[i]); |
| 238 | + } |
| 239 | + ans.push_back(move(t)); |
| 240 | + } |
| 241 | + return ans; |
| 242 | + } |
| 243 | +}; |
| 244 | +``` |
| 245 | +
|
| 246 | +<!-- tabs:end --> |
| 247 | +
|
| 248 | +<!-- solution:end --> |
| 249 | +
|
| 250 | +<!-- problem:end --> |
0 commit comments