Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
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 <= 10001 <= products[i].length <= 30001 <= sum(products[i].length) <= 2 * 104products are unique.products[i] consists of lowercase English letters.1 <= searchWord.length <= 1000searchWord consists of lowercase English letters.Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Trie
["mobile","mouse","moneypot","monitor","mousepad"] "mouse"
["havana"] "havana"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1268: Search Suggestions System
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) {
node.v.add(i);
}
}
}
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) {
t.add(products[i]);
}
ans.add(t);
}
return ans;
}
}
// Accepted solution for LeetCode #1268: Search Suggestions System
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
}
# Accepted solution for LeetCode #1268: Search Suggestions System
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)]
// Accepted solution for LeetCode #1268: Search Suggestions System
struct Solution;
use std::usize;
impl Solution {
fn suggested_products(mut products: Vec<String>, search_word: String) -> Vec<Vec<String>> {
let mut res = vec![];
products.sort_unstable();
let n = products.len();
let mut prefix = "".to_string();
let mut start = 0;
for c in search_word.chars() {
prefix.push(c);
start = start
+ products[start..]
.binary_search(&prefix)
.unwrap_or_else(|p| p);
let mut list: Vec<String> = vec![];
let end = usize::min(start + 3, n);
for i in start..end {
if products[i].starts_with(&prefix) {
list.push(products[i].clone());
}
}
res.push(list);
}
res
}
}
#[test]
fn test() {
let products = vec_string!["mobile", "mouse", "moneypot", "monitor", "mousepad"];
let search_word = "mouse".to_string();
let res: Vec<Vec<String>> = vec_vec_string![
["mobile", "moneypot", "monitor"],
["mobile", "moneypot", "monitor"],
["mouse", "mousepad"],
["mouse", "mousepad"],
["mouse", "mousepad"]
];
assert_eq!(Solution::suggested_products(products, search_word), res);
let products = vec_string!["havana"];
let search_word = "havana".to_string();
let res: Vec<Vec<String>> = vec_vec_string![
["havana"],
["havana"],
["havana"],
["havana"],
["havana"],
["havana"]
];
assert_eq!(Solution::suggested_products(products, search_word), res);
let products = vec_string!["bags", "baggage", "banner", "box", "cloths"];
let search_word = "bags".to_string();
let res: Vec<Vec<String>> = vec_vec_string![
["baggage", "bags", "banner"],
["baggage", "bags", "banner"],
["baggage", "bags"],
["bags"]
];
assert_eq!(Solution::suggested_products(products, search_word), res);
let products = vec_string!["havana"];
let search_word = "tatiana".to_string();
let res: Vec<Vec<String>> = vec_vec_string![[], [], [], [], [], [], []];
assert_eq!(Solution::suggested_products(products, search_word), res);
}
// Accepted solution for LeetCode #1268: Search Suggestions System
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1268: Search Suggestions System
// 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) {
// node.v.add(i);
// }
// }
// }
//
// 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) {
// t.add(products[i]);
// }
// ans.add(t);
// }
// return ans;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.