Command Palette

Search for a command to run...

Problem 40.12 · Designing Data StructuresHard

Design Search Autocomplete System

What it teaches: Autocomplete: keep the typed prefix, find matching past sentences, rank by popularity then alphabet, return the top 3; '#' saves the sentence.

Practise it on judges as “Design Search Autocomplete System”.

In plain words

When you type in a search box, after each letter it suggests the three most popular past searches that start with what you've typed. When you press Enter, your search is remembered so it can be suggested later.

Here, typing happens one character at a time with input(c). Return the top 3 matching sentences (most searched first; ties in alphabetical order). The character '#' means Enter: save the sentence and return an empty list.

The problem

Design AutocompleteSystem(String[] sentences, int[] times) with List<String> input(char c). Sentences are ranked by how many times they were entered (descending), then by ASCII order. Return at most 3. On '#', record the typed sentence (count + 1), reset the prefix and return [].

Example 1

Input: sentences = [i love you, island, iroman, i love leetcode], times = [5, 3, 2, 2]; input 'i', ' ', 'a', '#'
Output: [i love you, island, i love leetcode], [i love you, i love leetcode], [], []

Constraints

  • Up to 100 sentences, length ≤ 100
  • Up to 5000 input calls

Pattern clues in the wording

  • → Prefix queries
  • → Top-K by count with a tie-break
  • → State between calls (the current prefix)

These clues point to Combine Structures to Design: Pair a hash map (fast lookup) with a list, heap or tree (fast ordering) to meet every operation's time limit.

Stuck? Take one hint at a time

AutocompleteSystem · starter
import java.util.*;

class AutocompleteSystem {
    public AutocompleteSystem(String[] sentences, int[] times) {}
    public List<String> input(char c) { return new ArrayList<>(); }
}

Write your solution locally or in your editor for now. Pick Java, Python, C++, JavaScript or Go above: every solution on this page switches with it. The in-browser runner will run these tests right here.

Test cases

#InputExpected
1
ops = ["AutocompleteSystem","input","input","input","input"]
args = [[["i love you","island","iroman","i love leetcode"],[5,3,2,2]],["i"],[" "],["a"],["#"]]
[null,["i love you","island","i love leetcode"],["i love you","i love leetcode"],[],[]]

+ 1 hidden test the code runner will check

From slow to fast

Approaches

1

Counts map + filter and sort

Time O(S log S) per input for S sentences Space O(total characters)

On each character, scan all sentences that start with the prefix, sort by (−count, text), take 3. On '#', increment the count of the prefix and reset.

Approach 1
import java.util.*;

class AutocompleteSystem {
    private final Map<String, Integer> counts = new HashMap<>();
    private final StringBuilder prefix = new StringBuilder();

    public AutocompleteSystem(String[] sentences, int[] times) {
        for (int i = 0; i < sentences.length; i++) counts.merge(sentences[i], times[i], Integer::sum);
    }

    public List<String> input(char c) {
        if (c == '#') {
            counts.merge(prefix.toString(), 1, Integer::sum);
            prefix.setLength(0);
            return new ArrayList<>();
        }
        prefix.append(c);
        String p = prefix.toString();
        List<String> matches = new ArrayList<>();
        for (String s : counts.keySet()) if (s.startsWith(p)) matches.add(s);
        matches.sort((a, b) -> !counts.get(a).equals(counts.get(b)) ? counts.get(b) - counts.get(a) : a.compareTo(b));
        return new ArrayList<>(matches.subList(0, Math.min(3, matches.size())));
    }
}

Verdict: Simple and fast enough for these limits.

2

Trie walked character by character

Time O(M log M) per input for M matches Space O(total characters × depth)

Each trie node keeps the counts of sentences passing through it. Keep a pointer to the node for the current prefix and move it one character per input, so only matching sentences are ranked.

▶ Dry run: Each trie node stores the sentences passing through itsentences = [i love you, island, iroman, i love leetcode], times = [5, 3, 2, 2]; input 'i', ' ', 'a', '#'

node "i" counts(map)

i love you: 5island: 3iroman: 2i love leetcode: 2

returned(list)

[i love you, island, i love leetcode]

Step 1/4'i': move to the root's child i. All four sentences pass through it. Best 3 by count: 5, 3, then the tie at 2 goes to "i love leetcode" because a space comes before r in ASCII.

Approach 2
import java.util.*;

class AutocompleteSystem {
    private static class Node {
        final Map<Character, Node> next = new HashMap<>();
        final Map<String, Integer> counts = new HashMap<>();     // sentences passing through
    }

    private final Node root = new Node();
    private Node cur = root;
    private final StringBuilder prefix = new StringBuilder();

    public AutocompleteSystem(String[] sentences, int[] times) {
        for (int i = 0; i < sentences.length; i++) add(sentences[i], times[i]);
    }

    private void add(String s, int t) {
        Node n = root;
        for (char ch : s.toCharArray()) {
            n = n.next.computeIfAbsent(ch, k -> new Node());
            n.counts.merge(s, t, Integer::sum);
        }
    }

    public List<String> input(char c) {
        if (c == '#') {
            add(prefix.toString(), 1);
            prefix.setLength(0);
            cur = root;
            return new ArrayList<>();
        }
        prefix.append(c);
        cur = cur == null ? null : cur.next.get(c);
        if (cur == null) return new ArrayList<>();
        Map<String, Integer> m = cur.counts;
        PriorityQueue<String> pq = new PriorityQueue<>((a, b) -> !m.get(a).equals(m.get(b)) ? m.get(a) - m.get(b) : b.compareTo(a));
        for (String s : m.keySet()) {
            pq.offer(s);
            if (pq.size() > 3) pq.poll();                        // keep the best 3
        }
        LinkedList<String> out = new LinkedList<>();
        while (!pq.isEmpty()) out.addFirst(pq.poll());
        return out;
    }
}

Verdict: Scales when there are many sentences.

Before you submit

Edge cases and common mistakes

Test these inputs

  • No match for the prefix
  • Ties broken alphabetically (a space sorts before letters)
  • A new sentence entered with '#'

Mistakes people make

  • Forgetting to reset the prefix (and trie pointer) after '#'.

Interview

Follow-up questions

How does a real search box serve this to millions of users?