# 1268. Search Suggestions System

## Description

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"
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 * 104
• All the strings of products are unique.
• products[i] consists of lowercase English letters.
• 1 <= searchWord.length <= 1000
• searchWord consists of lowercase English letters.

## Solutions

Solution 1: Sorting + Trie

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.

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.

Each node of the trie maintains the following information:

• 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.
• 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.

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.

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.

• class Trie {
Trie[] children = new Trie[26];
List<Integer> v = new ArrayList<>();

public void insert(String w, int i) {
Trie node = this;
for (int j = 0; j < w.length(); ++j) {
int idx = w.charAt(j) - 'a';
if (node.children[idx] == null) {
node.children[idx] = new Trie();
}
node = node.children[idx];
if (node.v.size() < 3) {
}
}
}

public List<Integer>[] search(String w) {
Trie node = this;
int n = w.length();
List<Integer>[] ans = new List[n];
Arrays.setAll(ans, k -> new ArrayList<>());
for (int i = 0; i < n; ++i) {
int idx = w.charAt(i) - 'a';
if (node.children[idx] == null) {
break;
}
node = node.children[idx];
ans[i] = node.v;
}
return ans;
}
}

class Solution {
public List<List<String>> suggestedProducts(String[] products, String searchWord) {
Arrays.sort(products);
Trie trie = new Trie();
for (int i = 0; i < products.length; ++i) {
trie.insert(products[i], i);
}
List<List<String>> ans = new ArrayList<>();
for (var v : trie.search(searchWord)) {
List<String> t = new ArrayList<>();
for (int i : v) {
}
}
return ans;
}
}

• class Trie {
public:
void insert(string& w, int i) {
Trie* node = this;
for (int j = 0; j < w.size(); ++j) {
int idx = w[j] - 'a';
if (!node->children[idx]) {
node->children[idx] = new Trie();
}
node = node->children[idx];
if (node->v.size() < 3) {
node->v.push_back(i);
}
}
}

vector<vector<int>> search(string& w) {
Trie* node = this;
int n = w.size();
vector<vector<int>> ans(n);
for (int i = 0; i < w.size(); ++i) {
int idx = w[i] - 'a';
if (!node->children[idx]) {
break;
}
node = node->children[idx];
ans[i] = move(node->v);
}
return ans;
}

private:
vector<Trie*> children = vector<Trie*>(26);
vector<int> v;
};

class Solution {
public:
vector<vector<string>> suggestedProducts(vector<string>& products, string searchWord) {
sort(products.begin(), products.end());
Trie* trie = new Trie();
for (int i = 0; i < products.size(); ++i) {
trie->insert(products[i], i);
}
vector<vector<string>> ans;
for (auto& v : trie->search(searchWord)) {
vector<string> t;
for (int i : v) {
t.push_back(products[i]);
}
ans.push_back(move(t));
}
return ans;
}
};

• class Trie:
def __init__(self):
self.children: List[Union[Trie, None]] = [None] * 26
self.v: List[int] = []

def insert(self, w, i):
node = self
for c in w:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
if len(node.v) < 3:
node.v.append(i)

def search(self, w):
node = self
ans = [[] for _ in range(len(w))]
for i, c in enumerate(w):
idx = ord(c) - ord('a')
if node.children[idx] is None:
break
node = node.children[idx]
ans[i] = node.v
return ans

class Solution:
def suggestedProducts(
self, products: List[str], searchWord: str
) -> List[List[str]]:
products.sort()
trie = Trie()
for i, w in enumerate(products):
trie.insert(w, i)
return [[products[i] for i in v] for v in trie.search(searchWord)]


• type Trie struct {
children [26]*Trie
v        []int
}

func newTrie() *Trie {
return &Trie{}
}
func (this *Trie) insert(w string, i int) {
node := this
for _, c := range w {
c -= 'a'
if node.children[c] == nil {
node.children[c] = newTrie()
}
node = node.children[c]
if len(node.v) < 3 {
node.v = append(node.v, i)
}
}
}

func (this *Trie) search(w string) [][]int {
node := this
n := len(w)
ans := make([][]int, n)
for i, c := range w {
c -= 'a'
if node.children[c] == nil {
break
}
node = node.children[c]
ans[i] = node.v
}
return ans
}

func suggestedProducts(products []string, searchWord string) (ans [][]string) {
sort.Strings(products)
trie := newTrie()
for i, w := range products {
trie.insert(w, i)
}
for _, v := range trie.search(searchWord) {
t := []string{}
for _, i := range v {
t = append(t, products[i])
}
ans = append(ans, t)
}
return
}