Bird
Raised Fist0
Interview Prepchallenge-problemshardAmazonFacebookGoogleBloomberg

Word Search II

Choose your preparation mode4 modes available
▶
Steps
setup

Build Trie Root Node

Initialize the root of the trie as an empty node with no children and no word assigned.

💡 The trie root is the starting point for inserting all words, enabling efficient prefix searches.
Line:root = TrieNode()
💡 A single root node will hold all word prefixes, allowing shared traversal paths.
📊
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.
Step 1/20
·Active fill★Answer cell
setup
Result: []
insert
i
o
0
a
1
t
2
h
3
Result: []
insert
i
p
0
e
1
a
2
Result: []
insert
i
e
0
a
1
t
2
Result: []
insert
i
r
0
a
1
i
2
n
3
Result: []
compare
c
o
0
a
1
a
2
n
3
Result: []
move_right
c
#
0
a
1
a
2
n
3
Result: []
compare
r
#
0
c
a
1
a
2
n
3
Result: []
compare
e
0
c
t
1
a
2
e
3
Result: []
compare
e
0
r
t
1
c
a
2
e
3
Result: []
compare
i
0
h
1
c
k
2
r
3
Result: []
record
i
0
c
h
1
r
k
2
r
3
Result: ["oath"]
move_left
i
0
c
h
1
r
k
2
r
3
Result: ["oath"]
prune
Result: ["oath"]
compare
e
0
c
t
1
a
2
e
3
Result: ["oath"]
move_right
e
0
c
#
1
a
2
e
3
Result: ["oath"]
compare
e
0
r
#
1
c
a
2
e
3
Result: ["oath"]
compare
r
o
0
a
1
c
a
2
n
3
Result: ["oath"]
record
c
e
0
r
t
1
a
2
e
3
Result: ["oath","eat"]
reconstruct
Result: ["oath","eat"]

Key Takeaways

✓ Combining trie and backtracking efficiently prunes invalid search paths early.

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

  1. 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.
  2. 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.
  3. Final Answer:

    Option B -> Option B
  4. 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

  1. Step 1: Identify branching factor per digit

    At each digit (except the first), there are 4 choices: add '+', '-', '*', or concatenate (no operator).
  2. 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.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    4 choices per digit lead to exponential 4^n growth [OK]
Hint: Count operator choices plus concatenation per digit [OK]
Common Mistakes:
  • Confusing 3 operators only, ignoring concatenation
  • Assuming polynomial complexity
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

  1. Step 1: Identify missing empty trie check

    maxXor should return -1 if trie is empty (no children at root), but this check is missing.
  2. Step 2: Consequence of missing check

    Without this, traversal accesses null children causing runtime error or incorrect results when no nums ≤ m.
  3. Final Answer:

    Option A -> Option A
  4. 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

  1. Step 1: Identify which array is binary searched

    The algorithm always binary searches the smaller array of size m.
  2. 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))).
  3. Final Answer:

    Option A -> Option A
  4. 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

  1. Step 1: Identify problem with duplicate edges

    Repeated words can cause multiple identical edges, inflating in-degree counts incorrectly.
  2. 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.
  3. Step 3: Confirm BFS with correct in-degree works properly

    Maintains accurate topological order and cycle detection despite duplicates.
  4. Final Answer:

    Option A -> Option A
  5. 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 unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard