Trie Patterns: Mental Models & Intuition¶
Build deep understanding of when and why Tries work for string problems.
The Core Insight¶
A Trie is a tree where each path from root represents a prefix.
This simple structure enables O(L) operations where L is the string length - regardless of how many strings are stored!
Mental Model 1: The Branching Path¶
Imagine a filing cabinet where each drawer leads to more drawers:
root
/ \
a b
/ \ \
p t a
/ \ \
p e t(end)
/
l
/
e(end)
Words: "apple", "ate", "bat"
Finding "apple":
root β a β p β p β l β e β (found: is_end=True)
Finding "app":
root β a β p β p β NOT is_end β (prefix exists, not a word)
Finding "apt":
root β a β p β β (no 't' child)
Key insight: Shared prefixes share paths. "apple" and "application" share "appl".
Mental Model 2: The Autocomplete Tree¶
Think of how your phone suggests words:
You type: "m" β [mobile, money, monitor]
You type: "mo" β [mobile, money, monitor]
You type: "mou" β [mouse, mousepad]
The Trie naturally groups words by prefix:
m
/|\
o i a
/| | \
n u c t...
/| | |
e i s r...
| \ |
y t e
| \
pot pad
Each node in the trie represents a "state" in typing - where you are after typing certain characters.
Mental Model 3: Wildcard as Branching¶
For '.' wildcard search, think of it as taking ALL possible paths:
Words: "bad", "dad", "mad"
Search: ".ad"
root
/ | \
b d m
| | |
a a a
| | |
d d d
(end)(end)(end)
'.' at position 0 β Try ALL children (b, d, m)
Each path leads to 'a' β then 'd' β found!
It's like parallel universe exploration - the '.' splits reality.
Mental Model 4: Grid + Trie = Pruned Search¶
For Word Search II, visualize the trie as a "validity checker":
Board: Trie (words: ["oath", "eat"]):
o a a n root
e t a e / \
i h k r o e
i f l v / \
a a
/ \
t t
/ (end)
h
(end)
DFS from 'o':
Check: Is 'o' in trie? β β Continue
Move to 'a': Is 'a' child of 'o' in trie? β β Continue
Move to 't': Is 't' child of 'a' in trie? β β Continue
Move to 'h': Is 'h' child of 't' in trie? β + is_end β Found "oath"!
Without trie: Try all 10,000 words at each cell
With trie: Follow single path, prune instantly if no match
Mental Model 5: Shortest Prefix = First End¶
For Replace Words, you're looking for the "first exit":
Dictionary: ["cat", "ca"]
Word: "cattle"
Trie:
root β c β a(end) β t(end)
β
First end!
Traversing "cattle":
'c' β keep going
'a' β is_end=True! Stop here, return "ca"
The first is_end you hit is the shortest root.
Algorithm Selection Guide¶
| Problem Type | Pattern | Why |
|---|---|---|
| Exact word lookup | Basic Trie | O(L) guaranteed |
| Prefix checking | startsWith() | Natural trie operation |
| Wildcard search | DFS on Trie | Branch on '.' |
| Multi-word grid search | Trie + Backtracking | Prune invalid paths |
| Shortest prefix | Find first is_end | Early termination |
| Autocomplete | Store words at nodes | O(1) per prefix query |
Common Pitfalls¶
Pitfall 1: Confusing Prefix vs Word¶
# WRONG: Only checks if path exists
def search(self, word):
node = self._find_node(word)
return node is not None # "app" returns True even if only "apple" exists!
# CORRECT: Check is_end flag
def search(self, word):
node = self._find_node(word)
return node is not None and node.is_end
Pitfall 2: Forgetting to Mark End¶
# WRONG: No end marker
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
# Missing: node.is_end = True
# CORRECT: Mark word end
def insert(self, word):
# ... same as above ...
node.is_end = True # Essential!
Pitfall 3: Duplicates in Grid Search¶
# WRONG: Can find same word multiple times
if next_node.word:
result.append(next_node.word)
# Word stays, might be found again from different path
# CORRECT: Clear after finding
if next_node.word:
result.append(next_node.word)
next_node.word = None # Prevent duplicates
Pitfall 4: Not Pruning in Grid Search¶
# INEFFICIENT: Keep empty branches
if next_node.word:
result.append(next_node.word)
next_node.word = None
# OPTIMIZED: Prune empty branches
if next_node.word:
result.append(next_node.word)
next_node.word = None
if not next_node.children and not next_node.word:
del node.children[char] # Prune dead end
Practice Progression¶
Level 1: Basic Operations¶
- LC 208 - Implement Trie (Insert, search, prefix)
Level 2: Enhanced Search¶
- LC 211 - Add and Search Words (Wildcard with '.')
Level 3: Application¶
- LC 648 - Replace Words (Shortest prefix match)
- LC 1268 - Search Suggestions System (Autocomplete)
Level 4: Combination¶
- LC 212 - Word Search II (Trie + Backtracking)
Quick Reference Card¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRIE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STRUCTURE: INSERT: β
β ββββββββββ βββββββ β
β children = {} for char in word: β
β is_end = False if char not in children: β
β create node β
β move to child β
β mark is_end = True β
β β
β SEARCH WORD: SEARCH PREFIX: β
β ββββββββββββ βββββββββββββ β
β node = find_node(word) return find_node(prefix) β
β return node and node.is_end is not None β
β β
β WILDCARD '.': GRID + TRIE: β
β ββββββββββββ ββββββββββ β
β if char == '.': Build trie from words β
β try ALL children DFS grid, follow trie β
β else: Prune if no trie path β
β follow exact path Mark visited, backtrack β
β β
β COMPLEXITY: β
β ββββββββββ β
β Insert: O(L) β
β Search: O(L) exact, O(26^D Γ L) with D wildcards β
β Space: O(N Γ L) for N words β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
When to Use Trie vs Alternatives¶
Need string prefix operations?
β
ββ No β Hash Set (simpler for exact lookup)
β
ββ Yes
β
ββ Static data, few queries? β Sorted Array + Binary Search
β
ββ Dynamic data OR many queries? β Trie
β
ββ Need wildcards? β Trie + DFS
β
ββ Multiple strings in grid? β Trie + Backtracking
β
ββ Need suggestions? β Trie with word lists at nodes