Master this topic with zero to advance depth.
A Trie (pronounced "try"), also known as a Prefix Tree, is a specialized tree-based data structure used for efficient retrieval of keys in a large dataset of strings. Unlike a standard binary search tree, no node in the trie stores the key associated with that node; instead, its position in the tree defines the key with which it is associated.
Trie operations are highly efficient, with time complexity scales with the length of the string (), not the number of keys ().
| Operation | Description | Time Complexity | Space Complexity |
|---|---|---|---|
Insert(word) | Add a word to the trie. | worst case | |
Search(word) | Check if a word exists in the trie. | ||
StartsWith(pre) | Check if any word starts with prefix pre. |
While Hash Maps offer search on average, Tries provide unique advantages:
[26]: Fastest access ( to find child), but space-intensive if the alphabet is large or the trie is sparse.s.r).Implement Trie (Prefix Tree)
Implement a trie which supports insert, search, and startsWith operations.
Examples
Intuition
Flexible implementation using a HashMap for children, suitable for any character set (Unicode).
Detailed Dry Run
Insert 'apple': R -> a -> p -> p -> l -> e (end).
Intuition
Static array [26] for lowercase English. Fastest possible access with minimal memory overhead for dense tries.
Detailed Dry Run
Insert 'cat': root.children['c'-'a'] -> node1.children['a'-'a'] -> node2.children['t'-'a'] -> node3(isEnd=true).
Add and Search Word
Design a data structure that supports adding new words and finding if a string matches any previously added string, including wildcard character '.' matching.
Examples
Intuition
Standard Trie implementation. When encountering '.', perform DFS to check all possible children of the current node.
Detailed Dry Run
Search '.ad': Root -> dfs('.'). Try 'a'-'z'. 'b' exists. From 'b', dfs('ad') -> matches 'ad' and isEnd=true. Return true.
Map Sum Pairs
Calculate sum of all values whose key starts with a specific prefix.
Intuition
Each trie node stores the sum of values for all words passing through it. When updating a key, we first find its old value to calculate the delta and update all nodes in the path.
Detailed Dry Run
Insert('apple', 3): Nodes a,p,p,l,e each get +3 sum. Insert('app', 2): Nodes a,p,p get +2. Sum('ap') -> 5.
Replace Words
In English, we have a concept called root, which can be followed by some other word to form another longer word - let's call this word successor. For example, when the root "an" is followed by the successor word "other", we can form a new word "another".\n\nGiven a dictionary consisting of many roots and a sentence consisting of words separated by spaces, replace all the successors in the sentence with the root forming it. If a successor can be replaced by more than one root, replace it with the root that has the shortest length.
Examples
Intuition
Store all roots in a HashSet. For each word in the sentence, check every possible prefix (starting from length 1) in the HashSet. Return the first match found.
Detailed Dry Run
Word: 'cattle'. Check 'c', 'ca', 'cat'. 'cat' is in HashSet. Replace 'cattle' with 'cat'.
Intuition
Build a Trie from the roots. For each word in the sentence, traverse the Trie. The first isEndOfWord node encountered provides the shortest root.
Detailed Dry Run
Trie: root -> c -> a -> t (end). Word: 'cattle'. Traverse c -> a -> t (found end). Return 'cat'.
Word Search II
If we have words, running a standard Word Search (DFS) for each word takes . This is inefficient because many words share prefixes (e.g., 'apple' and 'apply').
By storing the dictionary in a Trie, we can search for all words simultaneously. As we traverse the grid in DFS, we also traverse the Trie. If a path in the grid doesn't match any prefix in the Trie, we prune the search immediately.
Once a word is found, we can remove it from the Trie (or mark it as found) and potentially delete leaf nodes that are no longer part of any other words. This drastically reduces the search space for subsequent cells.
Given an m x n board of characters and a list of strings words, return all words on the board.
Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. Includes visual Trie-based pruning trace. The same letter cell may not be used more than once in a word.
Examples
Intuition
Iterate through each word in the list and perform a standard DFS search on the board.
For each word in words, scan every cell (r, c). If board[r][c] == word[0], start a DFS to find the rest of the characters.
Detailed Dry Run
Searching 'oath'. Find 'o' at (0,0). Neighbors: (0,1) 'a'. Next: (1,1) 't'. Next (0,1) NO, (0,2) NO, (1,0) NO... SUCCESS.
Intuition
Insert all words into a Trie. Traverse the board and the Trie simultaneously to find matches.
Starting at each cell, if the character exists as a child of the Trie root, move into the Trie. If node.isEndOfWord, add the word to result.
Detailed Dry Run
Trie: root -> o -> a -> t -> h (END). At (0,0) 'o': matches root.child('o'). Neighbors of (0,0): 'a' at (0,1) matches 'o'.child('a'). Neighbors of (0,1): 't' at (1,1) matches 'a'.child('t')...
Intuition
When a leaf node is found and its word is extracted, if it has no children, we can delete the node from its parent to avoid re-visiting dead branches.
Add a parent pointer or count to each node. When word != "", set it to null. If children count becomes 0, remove the entry from search path completely.
Detailed Dry Run
Trie: root -> o -> a -> t -> h (END). After finding 'oath', the path 'h' -> 't' -> 'a' -> 'o' is pruned if no other words use them. Subsequent board scans will skip 'o' immediately.
Design Search Autocomplete System
Design a search autocomplete system for a search engine. Users may input a sentence (at least one character and end with a special character '#'). For each character they type except '#', you need to return the top 3 historical hot sentences that have a prefix the same as the part of the sentence already typed.
Typed: "i"
Prefix "i" matches "i love you" (5), "island" (3), "ironman" (2).
Return: ["i love you", "island", "ironman"]
Typed: " " (space)
Prefix "i " matches "i love you" (5), "i a cool" (2).
Return: ["i love you", "i a cool"]Examples
Intuition
Maintain a HashMap of historical sentences and their counts. For every input, iterate through the entire map, collect sentences starting with the prefix, and sort them to find the top 3. Simple but O(N * L) per query.
Intuition
Store each historical sentence in a Trie. Each node stores a list of the top 3 hot sentences passing through it. Update these lists incrementally to ensure O(1) query time (just return the list at the current node).
Word Squares
Given an array of unique strings words, return all the word squares you can build from words. The same word from words can be used multiple times. A sequence of words forms a valid word square if the kth row and kth column read the exact same string for each 0 <= k < max(numRows, numColumns).
Examples
Intuition
Try all possible word combinations recursively. At each step, check if the current partial square is valid. Very slow as it doesn't use the prefix property effectively.
Intuition
At each step k, we need to find words that start with a specific prefix. This prefix is formed by the characters at position k of the words already chosen. A Trie is the perfect data structure to quickly fetch all words sharing a common prefix.
Maximum XOR of Two Numbers in an Array
Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 <= i <= j < n.
Nums: [3, 10, 5, 25, 2, 8]
Max XOR: 5 XOR 25 = 28
Binary Trie approach:
1. Insert all numbers as 32-bit binary strings into a Trie.
2. For each number X, find a path that maximizes XOR.
3. To maximize XOR, at each bit, try to go to the opposite bit (if curr is 0, try 1).
If opposite exists, result |= (1 << bit).Examples
Intuition
Check every pair of numbers in the array and calculate their XOR. Keep track of the maximum XOR found. Simple but .
Intuition
Represent each number as a 31-bit or 32-bit binary string. Insert all numbers into a Trie where each node has at most two children (0 and 1). For each number , we want to find a number such that is maximized. At each bit of , if the -th bit is , we prioritize moving to a child representing in the Trie. This greedy choice at each bit ensures the maximum possible XOR value.
Camelcase Matching
A query word matches a given pattern if we can insert lowercase letters to the pattern so that it equals the query. Return a list of booleans.
Queries: ["FooBar", "FooBarTest", "FootBall"]
Pattern: "FB"
"FooBar" -> F-oo-B-ar -> Matches!
"FooBarTest" -> F-oo-B-ar-T-est -> Extra 'T' (Uppercase) -> Fails!Examples
Intuition
For each word, use two pointers to check if the pattern is a subsequence. Additionally, ensure that any uppercase letters in the word that are NOT in the pattern cause a mismatch.
Intuition
For each query, use two pointers ( for query, for pattern). If , increment both. If is lowercase, increment only . If is uppercase and doesn't match , it's a mismatch. After the loop, if reached pattern length and no extra uppercase letters remain in query, it's a match.
Prefix and Suffix Search
Design a data structure that allows searching for a word with a given prefix AND a given suffix. If there are multiple valid words, return the largest index.
Words: ["apple"]
Suffix#Prefix construction: "e#apple", "le#apple", "ple#apple", ...
Insert these into a Trie.
Query: prefix="ap", suffix="le"
Search Trie for "le#ap".
Found node stores max index: 0.Examples
Intuition
Pre-calculate all potential prefix + '#' + suffix combinations for every word and store them in a HashMap with their index. Query is O(1). Inefficient space but simple.
Intuition
To handle both prefix and suffix in a single search, we can insert multiple variations of each word into the Trie. For a word like 'apple', insert: '#apple', 'e#apple', 'le#apple', 'ple#apple', 'pple#apple', 'apple#apple'. A query with prefix 'a' and suffix 'le' becomes a prefix search for 'le#a' in this special Trie.
Word Ladder II
Given two words, beginWord and endWord, and a dictionary wordList, return all shortest transformation sequences from beginWord to endWord. Only one letter can be changed at a time.
Examples
Intuition
Use a standard BFS to find the shortest distance from beginWord to endWord. In each step, change one character at a time and check if it exists in the dictionary. This approach only finds the length of the shortest path, not the actual paths themselves.
Intuition
Use BFS to find the shortest distance from beginWord to all reachable words. To quickly find 'neighbors' (words differing by one char), we can iterate through each position of the word and try all 26 letters, checking against a Set/Trie. Finally, use DFS to backtrack all shortest paths from beginWord to endWord using the distance levels found in BFS.
Concatenated Words
Given an array of strings words (without duplicates), return all the concatenated words in the given list of words. A concatenated word is defined as a string that is entirely comprised of at least two shorter words in the given array.
Words: ["cat", "dog", "catdog"]
DFS(0):
1. Prefixes found: "cat" (Trie matches).
2. Remaining: "dog".
3. DFS("dog"): Prefix found: "dog". Remaining: "".
4. SUCCESS -> "catdog" is concatenated.Examples
Intuition
Use a HashSet to store all words. For each word, check if it can be partitioned into at least two shorter words present in the Set using a recursive DFS with memoization. Simple but less space-efficient for prefix matching.
Intuition
First, sort the words by length. This allows us to only search for a word's composition using words that were previously added to the Trie (which are guaranteed to be shorter). For each word, use DFS with a Trie to check if it can be broken into multiple pieces that are already in the Trie.
Palindrome Pairs
Given a list of unique words, return all pairs of distinct indices (i, j) such that the concatenation of the two words words[i] + words[j] is a palindrome.
Words: ["abcd", "dcba", "lls", "s", "sssll"]
Pairs:
1. "abcd" + "dcba" = "abcddcba" (Palindrome)
2. "dcba" + "abcd" = "dcbaabcd" (Palindrome)
3. "s" + "lls" = "slls" (Palindrome)
4. "sssll" + "lls" = "ssslllls" (Wait, no...)
Wait: "lls" + "s" = "llss" (No)
Actual: "sssll" + "lls" -> "ssslllls" NO.
Let's check "lls" reverse is "sll".
If we pick i="sssll", j="lls", words[i]+words[j] = "ssslllls" Fails.
Correct for "lls": s + lls = slls (Correct).Examples
Intuition
Iterate through every pair of indices in the array. Construct the string words[i] + words[j] and check if it is a palindrome. This is , which is too slow for large inputs.
Intuition
For a word , we want to find such that is a palindrome. This happens if can be split into where is a palindrome and is the reverse of , OR if where is a palindrome and is the reverse of . A Trie storing words in reverse allows us to efficiently find these matches.
Shortest Unique Prefix
Given an array of words, find the shortest unique prefix of each word, such that the prefix is not a prefix of any other word.
Words: ["zebra", "dog", "duck", "dove"]
Trie with counts:
root
-> z(1) -> e(1) ... -> found count=1 at 'z'
-> d(3)
-> o(2)
-> g(1) -> found count=1 at 'g' (prefix "dog")
-> v(1) -> found count=1 at 'v' (prefix "dov")
-> u(1) -> found count=1 at 'u' (prefix "du")Examples
Intuition
For each word, check every possible prefix starting from length 1. Compare this prefix against all other words. The first prefix that is not a prefix of any other word is the shortest unique prefix for that word.
Intuition
Insert all words into a Trie. As you insert, increment a visited count (count) for each node along the path. To find the shortest unique prefix for a word, traverse the Trie until you reach a node with count == 1. That node marks the end of the shortest unique prefix.
Maximum XOR with an Element from Array
Given an integer array nums, return the maximum value of nums[j] XOR xi where nums[j] <= mi. If all numbers in nums are larger than mi, the answer is -1.
Nums: [0, 1, 2, 3, 4]
Query: [3, 1] (x=3, m=1)
Valid nums <= 1: [0, 1]
XOR results: 3^0=3, 3^1=2
Max: 3
Optimal approach:
1. Sort nums.
2. Sort queries by m.
3. Insert nums into Trie as long as nums[j] <= m.
4. Query Trie for max XOR.Examples
Intuition
For each query [xi, mi], iterate through every number in the nums array. If nums[j] <= mi, calculate nums[j] XOR xi and update the maximum. Simple O(N*Q) solution.
Intuition
Since we need to check , we can sort both and (by ). By processing queries offline in non-decreasing order of , we only insert numbers into the Trie when they become 'valid' for the current . This transforms the conditional maximum problem into a standard Maximum XOR problem on a growing Trie.
Word Break II
Given a string s and a string dictionary wordDict, return all possible sentences that can be formed by adding spaces to s such that each word is a valid dictionary word.
s = "catsanddog", dict = ["cat","cats","and","sand","dog"]
Trie path 1: root -> c -> a -> t -> s (word) -> a -> n -> d (word) -> d -> o -> g (word)
Trie path 2: root -> c -> a -> t (word) -> s -> a -> n -> d (word) -> d -> o -> g (word)
Sentences:
1. "cats and dog"
2. "cat sand dog"Examples
Intuition
Iterate through the string s. If a prefix exists in the dictionary, recursively solve for the remaining suffix. O(2^N).
Intuition
Use a Trie to store words for fast prefix lookups. Combine DFS with memoization to store results for each starting index.
Boggle Game
Find the maximum number of words you can find in a Boggle board such that no two words overlap (i.e., they don't share any cell).
Board: ["abc", "def", "ghi"]
Words: ["abc", "defi", "gh"]
Possible Selections:
1. {"abc", "def"} -> 2 words
2. {"abc"} -> 1 word
Result: 2Examples
Intuition
For each word in the dictionary, perform a standard DFS on the board to see if it exists. Keep track of used cells and try all combinations of non-overlapping words. Very slow but simple.
Intuition
This is a variant of Word Search II. First, find all occurrences of all dictionary words on the board (Word Search II style). Then, treat this as a problem of finding the maximum number of disjoint sets (words) using backtracking. Each word is a set of cell coordinates.
Stream of Characters
Design a data structure that accepts a stream of characters and checks if any suffix of the characters queried so far is in a given list of words.
Examples
Intuition
Store all arrived characters in a list. For every query, iterate through all words in the dictionary and check if any word matches the current suffix of the character stream. O(N * L) per query.
Intuition
Since we need to match a suffix of the stream, it's efficient to store the dictionary words in a Trie in reversed order. As new characters arrive in the stream, we store them in a buffer (or current path). To check if any suffix matches, we traverse the reversed Trie starting from the last character received.
Longest Word in Dictionary through Deleting
Given a string s and a string dictionary dictionary, return the longest string in the dictionary that can be formed by deleting some characters of the given string s.
Examples
Intuition
Iterate through each word in the dictionary. Use two pointers to check if the word is a subsequence of s. Keep track of the longest word found (using lexicographical order for ties).
Intuition
Store s in a way that allows fast subsequence checks. For each character a-z, store a list of indices where it appears in s. For any word, use binary search to find the smallest valid index for the next character. A Trie of words can also be used to explore multiple words simultaneously.
Prefix and Suffix Search (Optimized Query)
The same as Prefix and Suffix Search, but focused on extreme query performance.
Intuition
For each query, iterate through the entire dictionary. For each word, check if it starts with the prefix and ends with the suffix. Return the maximum index found. O(N * L) per query.
Intuition
For cases where memory is available but query speed is critical, precompute all possible (prefix, suffix) pairs for every word. Use a HashMap to store prefix + " " + suffix as the key and the max index as the value.
Help us improve! Report bugs or suggest new features on our Telegram group.