A trie (prefix tree) stores strings character by character, so words that share a prefix share the path for it. Walking down one node per character answers "is this a word?" and "does any word start with this?" in time proportional to the length of the query, no matter how many words are stored. It's the tool for prefix questions; for plain membership, a hash set is usually better.
The idea#
Each node holds a map from character to child and a flag that says "a word ends here". Inserting a word walks down its characters, creating missing children, and sets the flag at the last node. Searching walks the same path:
- Whole-word search succeeds only if the path exists and the last node is flagged, so inserting
appledoes not makeappa word. - Prefix search succeeds as soon as the path exists; the flag doesn't matter.
Each operation is one walk down the tree, so its cost depends on the length of the word, not on how many words are stored.
| Operation | Time | Why |
|---|---|---|
| Insert | one step per character of the word | |
| Search | one node per character | |
| Starts with | the same walk, without checking the end flag |
Space is , where is the total number of characters inserted, minus whatever is shared.
The flag is one kind of marker. For other problems, store what the search needs inside the node: a count of words passing through, the word itself, a running sum, or a short list of suggestions. Then decide while walking, using those markers.
How to spot it#
Reach for a trie when the question is about prefixes over many strings.
- "Starts with", "prefix", "autocomplete", "suggestions".
- Many words with shared beginnings, and many queries against them.
- Replacing a word with its shortest dictionary root.
- Searching a grid or pattern where you want to stop as soon as no word can match.
When a trie is the wrong tool#
Prefix signals can mislead you, because a trie has real costs. In these situations they are not worth paying:
| Situation | Why the trie loses | Use instead |
|---|---|---|
| Only exact lookups | A hash set is also , with far less overhead | set |
| Memory is tight | Every node carries its own child map | set or a sorted list |
| Few, short strings | Nodes are scattered in memory, so lookups miss the cache | sorted list + binary search |
| Little shared prefix | No sharing means a sparse tree of mostly wasted nodes | set |
The simple rule: need prefix operations, use a trie; only need exact matches, use a hash set. Longest Common Prefix (LC 14) is a good example of a problem that looks like trie territory but isn't. Its hint below says why.
Template#
Here are insert and both searches, with the node layout from "The idea". Notice that one shared walk serves both kinds of search.
node: children = empty map, is_end = false
insert(word):
node = root
for each ch in word:
if ch not in node.children: create a child for ch
node = node.children[ch]
node.is_end = true # marker: a word ends here
walk(s): # the node for s, or nothing
node = root
for each ch in s:
if ch not in node.children: return nothing
node = node.children[ch]
return node
search(word): walk(word) exists and its is_end is true
starts_with(prefix): walk(prefix) exists
A map for the children means the alphabet does not need to be fixed. Create a child only when it is missing.
Pitfalls#
These mistakes cause the most wrong answers.
- Treating a prefix as a word.
searchmust check the end marker;starts_withmust not. - Overwriting an existing child. Creating a new child without checking first discards everything below it. Check for the key, or use
setdefault. - Using a trie for exact membership. If no prefix question is ever asked, a
setis simpler and smaller. - Clumsy build orders. When a word must be built from shorter words, sorting and inserting one by one while checking end flags is awkward. Build the whole trie first, then walk it.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first one.
- 14. Longest Common Prefix
- 208. Implement Trie (Prefix Tree)
- 720. Longest Word in Dictionary
- 648. Replace Words
- 677. Map Sum Pairs
- 2707. Extra Characters in a String
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Implement Trie (Prefix Tree)
Write the shared walk once. search is the walk plus the end flag; starts_with is the walk alone.
Hint: Longest Word in Dictionary
Insert every word first. Then DFS from the root, but step only into children whose is_end is true. The deepest node reached is the answer; break ties by the smaller word. For small inputs, a set of words that checks every prefix also works.
Hint: Replace Words
Walk each sentence word down the trie of roots. Stop at the first node with an end marker: that is the shortest root. No marker on the path means the word stays as it is.
Hint: Map Sum Pairs
Store a running sum at every node: the total value of keys that pass through it. A prefix query is then one walk and one read. When a key is inserted again, add only the difference from its old value.
Hint: Extra Characters in a String
This is string DP over positions. From each position i, walk the trie forward and stop the moment no dictionary word continues. That replaces testing every substring.
Hint: Longest Common Prefix
This looks like trie territory, but it isn't. Compare the strings column by column and stop at the first mismatch. It is simpler and as fast.
Key takeaways#
- A trie shares prefixes, so insert, search and prefix checks are regardless of how many words are stored.
- Whole-word search checks the end marker; prefix search doesn't.
- Store whatever the problem needs in the node: a flag, a count, a word, a sum, suggestions.
- Need prefix operations? Trie. Only exact matches? Hash set.