Word Search in Grid Using Backtracking in DSA Go - Time & Space Complexity
We want to understand how the time needed to find a word in a grid grows as the grid or word size increases.
Specifically, how does the search process scale when we try to find the word by exploring many paths?
Analyze the time complexity of the following code snippet.
func exist(board [][]byte, word string) bool {
rows, cols := len(board), len(board[0])
var backtrack func(r, c, i int) bool
backtrack = func(r, c, i int) bool {
if i == len(word) { return true }
if r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] != word[i] { return false }
temp := board[r][c]
board[r][c] = '#'
found := backtrack(r+1, c, i+1) || backtrack(r-1, c, i+1) || backtrack(r, c+1, i+1) || backtrack(r, c-1, i+1)
board[r][c] = temp
return found
}
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
if backtrack(r, c, 0) { return true }
}
}
return false
}
This code tries to find a word in a 2D grid by exploring all possible paths using backtracking.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive backtracking exploring up to 4 directions from each cell.
- How many times: For each cell in the grid (rows * cols), the recursion can explore up to 4 branches for each character in the word.
As the grid size or word length grows, the number of paths to explore grows very fast.
| Input Size (n = word length) | Approx. Operations |
|---|---|
| 10 | Up to 4^10 * (rows * cols) |
| 100 | Up to 4^100 * (rows * cols) (very large) |
| 1000 | Up to 4^1000 * (rows * cols) (practically impossible) |
Pattern observation: The operations grow exponentially with the word length, making the search very costly for long words.
Time Complexity: O(rows * cols * 4^n)
This means the time grows very fast as the word length increases, because from each cell we explore up to 4 directions for each letter.
[X] Wrong: "The search only checks each cell once, so it is just O(rows * cols)."
[OK] Correct: The search explores many paths recursively for each letter, so it can revisit cells in different paths, causing exponential growth.
Understanding this complexity helps you explain why backtracking can be slow and when it is suitable, showing your grasp of algorithm behavior in real problems.
"What if we limited the search to only move right and down? How would the time complexity change?"
Practice
Solution
Step 1: Understand backtracking in word search
Backtracking tries all possible paths in the grid to find the target word by exploring neighbors recursively.Step 2: Eliminate other options
Sorting, counting, or replacing characters are unrelated to the search process using backtracking.Final Answer:
To explore all possible paths to find the word -> Option CQuick Check:
Backtracking = Explore paths [OK]
- Thinking backtracking sorts or modifies the grid
- Confusing backtracking with simple iteration
- Assuming backtracking counts words directly
Solution
Step 1: Understand grid indexing in Go
Grid indices start at 0 and go up to length-1, so valid indices satisfy 0 <= row < len(grid) and 0 <= col < len(grid[0]).Step 2: Check each option
if row >= 0 && row < len(grid) && col >= 0 && col < len(grid[0]) correctly uses >= 0 and < length. Options A, C, D use incorrect bounds like > 0 or <= length which are off by one.Final Answer:
if row >= 0 && row < len(grid) && col >= 0 && col < len(grid[0]) -> Option DQuick Check:
Index check = if row >= 0 && row < len(grid) && col >= 0 && col < len(grid[0]) [OK]
- Using > 0 instead of >= 0 excludes first row/column
- Using <= length allows out-of-range index
- Confusing row and column lengths
grid := [][]byte{{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}}
word := "ABCCED"
found := exist(grid, word)
fmt.Println(found)
func exist(board [][]byte, word string) bool {
for i := 0; i < len(board); i++ {
for j := 0; j < len(board[0]); j++ {
if backtrack(board, word, i, j, 0) {
return true
}
}
}
return false
}
func backtrack(board [][]byte, word string, row, col, index int) bool {
if index == len(word) {
return true
}
if row < 0 || row >= len(board) || col < 0 || col >= len(board[0]) || board[row][col] != word[index] {
return false
}
temp := board[row][col]
board[row][col] = '#'
found := backtrack(board, word, row+1, col, index+1) ||
backtrack(board, word, row-1, col, index+1) ||
backtrack(board, word, row, col+1, index+1) ||
backtrack(board, word, row, col-1, index+1)
board[row][col] = temp
return found
}Solution
Step 1: Understand the grid and word
The grid contains the letters to form the word "ABCCED" starting at (0,0) moving right and down.Step 2: Trace backtracking calls
Backtracking explores neighbors and marks visited cells with '#'. The word "ABCCED" can be found following the path A->B->C->C->E->D.Final Answer:
true -> Option AQuick Check:
Word found = true [OK]
- Not marking visited cells causing revisits
- Incorrect boundary checks causing false negatives
- Assuming word is not found without tracing path
func backtrack(board [][]byte, word string, row, col, index int) bool {
if index == len(word) {
return true
}
if row < 0 || row >= len(board) || col < 0 || col >= len(board[0]) || board[row][col] != word[index] {
return false
}
// Missing marking visited cell
found := backtrack(board, word, row+1, col, index+1) ||
backtrack(board, word, row-1, col, index+1) ||
backtrack(board, word, row, col+1, index+1) ||
backtrack(board, word, row, col-1, index+1)
return found
}Solution
Step 1: Analyze the backtrack function
The function checks boundaries and matches characters but does not mark visited cells, so it can revisit the same cell multiple times.Step 2: Understand the impact of missing visited marking
Without marking visited cells, the search may loop or fail to find the word correctly because it revisits cells already used in the current path.Final Answer:
The function does not mark visited cells, causing revisits -> Option AQuick Check:
Missing visited marking = The function does not mark visited cells, causing revisits [OK]
- Ignoring visited marking causes infinite loops
- Misunderstanding base case logic
- Assuming boundary checks fix revisits
Solution
Step 1: Understand the problem of multiple words
Searching each word separately is inefficient because it repeats similar searches multiple times.Step 2: Use Trie with backtracking
Building a Trie (prefix tree) of all words allows pruning paths early if no word matches, improving efficiency significantly.Step 3: Eliminate incorrect options
Sorting or replacing letters does not help search. Running backtracking separately is slow.Final Answer:
Build a Trie of words and use backtracking with pruning to find all words -> Option BQuick Check:
Trie + backtracking = efficient multi-word search [OK]
- Searching each word independently wastes time
- Ignoring Trie data structure benefits
- Trying to sort grid which doesn't help search
Ready to prove it?
Pass the test and this pattern moves unknown
