Design Add and Search Words Data Structure

MediumTrie · DFS · Design🧩 TrieLeetCode #211 ↗Updated

Problem

Design WordDictionary with addWord(word), which stores a lowercase word, and search(pattern), which returns true if some stored word matches the pattern. A pattern has the same length as the word it matches; each '.' matches any one letter, every other letter must match exactly.
Inputadd bad, dad, mad; search("pad")Outputfalse
Inputsearch("bad") search(".ad")Outputtrue, true
Inputsearch("b..") search("b.")Outputtrue, false

1 ≤ word.length ≤ 25  ·  added words are lowercase  ·  at most 2 dots per search  ·  ≤ 104 calls

Try it yourself

Java
class WordDictionary {
    public void addWord(String word) {
        
    }

    public boolean search(String word) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Store words in a trie: a letter follows exactly one edge, and only a '.' branches into all existing children. Prefixes that no word has are pruned immediately instead of being compared word by word.
PatternTrie

Instinct Many words, shared prefixes, and questions like "starts with", autocomplete, wildcard match or "search a board for all these words" - build a prefix tree.

Memory hack Words share their beginnings, so store each beginning once - walk one letter per step, and an end flag marks where a real word stops.

Open the Trie guide →

Approaches

Brute ForceList + Compare Every Word

Time O(1) add, O(N·L) searchSpace O(S)

Keep all words in a list and compare the pattern with each one, letter by letter, treating '.' as a wildcard. N = words, L = length, S = total characters.

Java
class WordDictionary {
    private final List<String> words = new ArrayList<>();

    public void addWord(String word) { words.add(word); }

    public boolean search(String word) {
        for (String w : words) if (matches(w, word)) return true;
        return false;
    }

    private boolean matches(String w, String p) {
        if (w.length() != p.length()) return false;
        for (int i = 0; i < w.length(); i++)
            if (p.charAt(i) != '.' && p.charAt(i) != w.charAt(i)) return false;
        return true;
    }
}

Trivial to write, but every search touches the whole dictionary.

At a glance

LevelApproachTimeSpace
Brute ForceList + Compare Every WordO(1) add, O(N·L) searchO(S)
BetterHash Sets Bucketed by LengthO(L) add, O(N·L) searchO(S)
OptimalTrie + DFS on WildcardsO(L) add, O(26d·L) searchO(26·S)
Common pitfalls
  • Skipping the isEnd check — "b." would wrongly match via "bad".
  • Regex-matching every word — still O(N·L), plus regex overhead.
  • Indexing '.' - 'a' — negative index; handle the wildcard first.
Interview tips
  • Start from the list scan, then ask how to stop comparing words that share no prefix.
  • Reuse the Trie from LC 208 — only search changes.
  • State the bounded branching: at most 2 dots ⇒ at most 262 paths.
Pattern
Trie + backtracking → dictionary lookups with wildcards or many patterns sharing prefixes.
My notes saved in this browser