💡 Words are recorded exactly once during backtracking.
reconstruct
Final Result After Full Board Search
After backtracking from all cells, the final list of found words is ['oath', 'eat'].
💡 The algorithm completes after exploring all starting points and pruning the trie.
Line:return res
💡 The algorithm efficiently finds all valid words on the board.
from typing import List
class TrieNode:
def __init__(self):
self.children = {}
self.word = None
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
root = TrieNode() # STEP 1
for word in words: # STEP 2-5
node = root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.word = word
ROWS, COLS = len(board), len(board[0])
res = []
def backtrack(r, c, parent):
letter = board[r][c]
curr_node = parent.children.get(letter)
if not curr_node:
return # STEP 11
if curr_node.word:
res.append(curr_node.word) # STEP 12
curr_node.word = None
board[r][c] = '#' # STEP 7, 16
for (nr, nc) in [(r+1,c),(r-1,c),(r,c+1),(r,c-1)]:
if 0 <= nr < ROWS and 0 <= nc < COLS and board[nr][nc] != '#':
backtrack(nr, nc, curr_node) # STEP 8,9,10,17,18,19
board[r][c] = letter # STEP 13
if not curr_node.children:
parent.children.pop(letter) # STEP 14
for r in range(ROWS): # STEP 6,15
for c in range(COLS):
backtrack(r, c, root)
return res # STEP 20
📊
Word Search II - Watch the Algorithm Execute, Step by Step
Watching each step of the backtracking combined with trie traversal reveals how the algorithm efficiently finds words without redundant searches.
This pruning is hard to see from code alone but is clear when watching trie traversal and cell marking.
✓ In-place marking of visited cells avoids extra space and prevents cycles during search.
Visualizing the board state changing clarifies how the algorithm avoids revisiting cells.
✓ Dynamic pruning of trie nodes after word discovery optimizes subsequent searches.
Seeing trie nodes removed after word found shows how the search space shrinks over time.
Practice
(1/5)
1. Given the following Python code snippet implementing the optimized trie approach, what is the output of maximizeXor(nums, queries) for nums = [0,1,2] and queries = [[3,1],[1,2]]?
class TrieNode:
def __init__(self):
self.children = [null, null]
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, num):
node = self.root
for i in range(31, -1, -1):
bit = (num >> i) & 1
if not node.children[bit]:
node.children[bit] = TrieNode()
node = node.children[bit]
def maxXor(self, num):
node = self.root
if not node.children[0] and not node.children[1]:
return -1
max_xor = 0
for i in range(31, -1, -1):
bit = (num >> i) & 1
toggled_bit = 1 - bit
if node.children[toggled_bit]:
max_xor |= (1 << i)
node = node.children[toggled_bit]
else:
node = node.children[bit]
return max_xor
def maximizeXor(nums, queries):
nums.sort()
queries = sorted([(x, m, i) for i, (x, m) in enumerate(queries)], key=lambda x: x[1])
res = [-1] * len(queries)
trie = Trie()
idx = 0
n = len(nums)
for x, m, i in queries:
while idx < n and nums[idx] <= m:
trie.insert(nums[idx])
idx += 1
res[i] = trie.maxXor(x)
return res
print(maximizeXor(nums, queries))
easy
A. [3, 3]
B. [2, 3]
C. [3, 1]
D. [-1, 3]
Solution
Step 1: Process first query (3,1)
Only nums ≤ 1 are [0,1]. Insert 0 and 1 into trie. maxXor(3) with {0,1} is max(3^0=3,3^1=2)=3.
Step 2: Process second query (1,2)
Now nums ≤ 2 are [0,1,2]. Insert 2. maxXor(1) with {0,1,2} is max(1^0=1,1^1=0,1^2=3)=3.
Final Answer:
Option B -> Option B
Quick Check:
Results match expected XOR values [OK]
Hint: Insert nums ≤ m incrementally, then maxXor query [OK]
Common Mistakes:
Forgetting to sort queries and nums
Not inserting all eligible nums before query
Returning -1 when trie is empty incorrectly
2. What is the worst-case time complexity of the optimal backtracking solution for the Expression Add Operators problem, given a numeric string of length n?
medium
A. O(n^3) due to nested loops and substring parsing
B. O(2^n) because each digit can either be concatenated or separated by an operator
C. O(4^n) because each digit can be followed by 4 choices: +, -, *, or no operator (concatenate)
D. O(3^n) since only three operators are inserted between digits
Solution
Step 1: Identify branching factor per digit
At each digit (except the first), there are 4 choices: add '+', '-', '*', or concatenate (no operator).
Step 2: Calculate total combinations
With n digits, the number of expressions is roughly 4^(n-1), which is O(4^n) in worst case.
Final Answer:
Option C -> Option C
Quick Check:
4 choices per digit lead to exponential 4^n growth [OK]
Hint: Count operator choices plus concatenation per digit [OK]
3. Examine the following buggy code snippet for the optimized trie approach. Which line contains the subtle bug that can cause incorrect results or runtime errors when no elements in nums are less than or equal to m in a query?
class TrieNode:
def __init__(self):
self.children = [null, null]
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, num):
node = self.root
for i in range(31, -1, -1):
bit = (num >> i) & 1
if not node.children[bit]:
node.children[bit] = TrieNode()
node = node.children[bit]
def maxXor(self, num):
node = self.root
# Bug: missing check for empty trie
max_xor = 0
for i in range(31, -1, -1):
bit = (num >> i) & 1
toggled_bit = 1 - bit
if node.children[toggled_bit]:
max_xor |= (1 << i)
node = node.children[toggled_bit]
else:
node = node.children[bit]
return max_xor
# rest of code omitted for brevity
medium
A. Line missing check if trie is empty before traversal in maxXor
B. Line 'if not node.children[bit]: node.children[bit] = TrieNode()' in insert method
C. Line with 'node = self.root' in maxXor method
D. Line 'node = node.children[bit]' in else branch of maxXor
Solution
Step 1: Identify missing empty trie check
maxXor should return -1 if trie is empty (no children at root), but this check is missing.
Step 2: Consequence of missing check
Without this, traversal accesses null children causing runtime error or incorrect results when no nums ≤ m.
Final Answer:
Option A -> Option A
Quick Check:
Adding 'if not node.children[0] and not node.children[1]: return -1' fixes bug [OK]
Hint: Always check if trie is empty before querying [OK]
Common Mistakes:
Forgetting empty trie check
Incorrect bit indexing
Misplaced node updates
4. What is the time complexity of the optimal binary search partition approach to find the median of two sorted arrays of sizes m and n (m ≤ n)?
medium
A. O(log(min(m, n)))
B. O(m + n)
C. O(log(m + n))
D. O(log(m))
Solution
Step 1: Identify which array is binary searched
The algorithm always binary searches the smaller array of size m.
Step 2: Analyze binary search complexity
Binary search on array of size m takes O(log m) time. Since m ≤ n, this is O(log(min(m, n))).
Final Answer:
Option A -> Option A
Quick Check:
Binary search on smaller array yields O(log(min(m,n))) [OK]
Hint: Binary search on smaller array size defines complexity [OK]
Common Mistakes:
Assuming O(m + n) due to merging intuition
Confusing log(m + n) with log(min(m, n))
Ignoring that binary search is only on smaller array
5. Suppose the Alien Dictionary problem is extended so that words can be repeated multiple times in the input list, possibly introducing duplicate edges. Which modification to the optimal Kahn's algorithm approach correctly handles this scenario without corrupting the in-degree counts?
hard
A. Use a set to track edges and only add an edge and increment in-degree if it is not already present
B. Reset in-degree counts after building the graph to avoid duplicates
C. Remove the edge deduplication check and increment in-degree for every edge found
D. Use DFS instead of BFS to avoid counting duplicate edges
Solution
Step 1: Identify problem with duplicate edges
Repeated words can cause multiple identical edges, inflating in-degree counts incorrectly.
Step 2: Use edge deduplication to prevent multiple increments
Track edges in a set and only add/increment if edge is new, preserving correct in-degree.
Step 3: Confirm BFS with correct in-degree works properly
Maintains accurate topological order and cycle detection despite duplicates.
Final Answer:
Option A -> Option A
Quick Check:
Edge deduplication is essential for correctness with repeated words [OK]
Hint: Avoid incrementing in-degree multiple times for same edge [OK]
Common Mistakes:
Ignoring duplicates
Resetting in-degree incorrectly
Switching to DFS unnecessarily
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.