Trie· 25 min

Search Suggestions System

LeetCode #1268 · Medium · Solution walkthrough with Java and Python code

Trie · Medium

LeetCode 1268: Search Suggestions System — solution

Sort the products once, then for each typed prefix binary-search the first match and take up to three neighbours. O(n log n * L) for sorting plus O(m * (log n + L)) per query loop, with O(1) extra space besides the output.

Problem in plain words

You have a list of product names and a word that the user types one letter at a time. After each letter, suggest up to three products that start with what has been typed so far, choosing the alphabetically smallest ones. Return the list of suggestions for every step.

For example, with products ["pen", "pencil", "pear"] and the word "pe", both steps suggest ["pear", "pen", "pencil"].

Intuition

After sorting, every product that starts with a given prefix sits in one contiguous block, and the block starts at the first position where the prefix would be inserted. So for each prefix, binary-search that position and look at the next three products; any of them that still start with the prefix are the answer. A trie also solves this, but sorting plus binary search is shorter to write and uses far less memory.

Approach

  1. Sort products lexicographically.
  2. For each length i from 1 to searchWord.length, let prefix be the first i letters.
  3. Find start, the first index whose product is greater than or equal to prefix (a lower bound). Because prefixes only grow, the search can begin from the previous start.
  4. Collect products at start, start + 1, start + 2 while they exist and start with prefix.
  5. Append that list (possibly empty) to the result.

Dry run

Input: products ["bean", "bagel", "banana", "bolt", "bake"], word "bak". Sorted: bagel, bake, banana, bean, bolt.

PrefixLower boundNext three that match
b0 (bagel)bagel, bake, banana
ba0 (bagel)bagel, bake, banana
bak1 (bake)bake (banana does not start with bak)

Java solution

java
class Solution {
    public List<List<String>> suggestedProducts(String[] products, String searchWord) {
        Arrays.sort(products);
        List<List<String>> result = new ArrayList<>();
        int start = 0;
        for (int i = 1; i <= searchWord.length(); i++) {
            String prefix = searchWord.substring(0, i);
            start = lowerBound(products, start, prefix);
            List<String> suggestions = new ArrayList<>();
            for (int j = start; j < products.length && j < start + 3; j++) {
                if (!products[j].startsWith(prefix)) break;
                suggestions.add(products[j]);
            }
            result.add(suggestions);
        }
        return result;
    }

    // First index in [from, n) whose value is >= target.
    private int lowerBound(String[] arr, int from, String target) {
        int lo = from, hi = arr.length;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            if (arr[mid].compareTo(target) < 0) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        return lo;
    }
}

Python solution

python
import bisect


class Solution:
    def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]:
        products.sort()
        result = []
        start = 0
        prefix = ""
        for ch in searchWord:
            prefix += ch
            start = bisect.bisect_left(products, prefix, start)
            suggestions = []
            for word in products[start:start + 3]:
                if not word.startswith(prefix):
                    break
                suggestions.append(word)
            result.append(suggestions)
        return result

Complexity

MeasureCostWhy
TimeO(n log n * L + m * L * log n)Sorting compares strings of length up to L; each of the m prefixes then does a binary search with string comparisons.
SpaceO(1) extraSorting is in place apart from the language's sort buffer; the output holds at most 3m strings.

Edge cases and common mistakes

  • No match: once a prefix matches nothing, every later step must still add an empty list, so the result always has searchWord.length entries.
  • Reusing `start`: it is safe because a longer prefix can never sort before a shorter one it extends.
  • Taking three neighbours without checking: products after start may not share the prefix; always verify with startsWith.
  • Duplicates in the input: identical product names are allowed and both appear in the suggestions.
›When is the trie version better?

If the product list is fixed and you serve many different search words, a trie that stores the top three words at each node answers every keystroke in O(1) after an up-front build.