125 lines
4.1 KiB
YAML
125 lines
4.1 KiB
YAML
title: Word Search
|
||
slug: word-search
|
||
difficulty: medium
|
||
leetcode_id: 79
|
||
leetcode_url: https://leetcode.com/problems/word-search/
|
||
categories:
|
||
- arrays
|
||
- recursion
|
||
patterns:
|
||
- backtracking
|
||
- dfs
|
||
|
||
description: |
|
||
Given an m x n grid of characters `board` and a string `word`, return true if `word` exists
|
||
in the grid.
|
||
|
||
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells
|
||
are horizontally or vertically neighboring. The same letter cell may not be used more than once.
|
||
|
||
constraints: |
|
||
- m == board.length
|
||
- n == board[i].length
|
||
- 1 <= m, n <= 6
|
||
- 1 <= word.length <= 15
|
||
- board and word consist of only lowercase and uppercase English letters
|
||
|
||
examples:
|
||
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"'
|
||
output: "true"
|
||
explanation: "Path exists starting from top-left corner."
|
||
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"'
|
||
output: "true"
|
||
explanation: "Path exists."
|
||
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"'
|
||
output: "false"
|
||
explanation: "Would need to reuse 'B' cell."
|
||
|
||
explanation:
|
||
approach: |
|
||
1. For each cell, try to start the word from there
|
||
2. Use DFS with backtracking to explore all paths
|
||
3. Mark cells as visited during exploration
|
||
4. Unmark cells when backtracking (restore state)
|
||
5. If entire word is matched, return true
|
||
|
||
intuition: |
|
||
This is a classic backtracking problem. We explore paths character by character,
|
||
and if we reach a dead end (no valid next character), we backtrack and try a
|
||
different direction.
|
||
|
||
The key is marking cells as visited during exploration to avoid reusing them,
|
||
then unmarking when we backtrack to allow other paths to use them.
|
||
|
||
common_pitfalls:
|
||
- title: Not restoring visited state
|
||
description: |
|
||
After exploring a path, you must unmark the cell as visited.
|
||
Otherwise, other paths from earlier cells can't use it.
|
||
wrong_approach: "Only marking, never unmarking"
|
||
correct_approach: "Mark before recursion, unmark after"
|
||
|
||
- title: Modifying board permanently
|
||
description: |
|
||
If you change board[r][c] to mark as visited, restore it after backtracking.
|
||
|
||
- title: Checking word completion too late
|
||
description: |
|
||
Check if entire word is matched (index == len(word)) at the start of DFS,
|
||
before any bounds/character checks.
|
||
|
||
key_takeaways:
|
||
- Backtracking = DFS with state restoration
|
||
- Mark and unmark visited cells around recursive calls
|
||
- Early termination when full word is found
|
||
- Grid constraints allow brute force (small board size)
|
||
|
||
time_complexity: "O(m × n × 3^L)"
|
||
space_complexity: "O(L)"
|
||
complexity_explanation: |
|
||
Time: Start from each cell, explore up to 3 directions (not the one we came from) for L characters.
|
||
Space: Recursion depth is at most word length L.
|
||
|
||
solutions:
|
||
- approach_name: DFS with Backtracking (Optimal)
|
||
is_optimal: true
|
||
code: |
|
||
def exist(board: list[list[str]], word: str) -> bool:
|
||
rows, cols = len(board), len(board[0])
|
||
|
||
def dfs(r: int, c: int, i: int) -> bool:
|
||
if i == len(word):
|
||
return True
|
||
|
||
if r < 0 or r >= rows or c < 0 or c >= cols:
|
||
return False
|
||
if board[r][c] != word[i]:
|
||
return False
|
||
|
||
# Mark as visited
|
||
temp = board[r][c]
|
||
board[r][c] = '#'
|
||
|
||
# Explore all 4 directions
|
||
found = (
|
||
dfs(r + 1, c, i + 1) or
|
||
dfs(r - 1, c, i + 1) or
|
||
dfs(r, c + 1, i + 1) or
|
||
dfs(r, c - 1, i + 1)
|
||
)
|
||
|
||
# Restore (backtrack)
|
||
board[r][c] = temp
|
||
|
||
return found
|
||
|
||
for r in range(rows):
|
||
for c in range(cols):
|
||
if dfs(r, c, 0):
|
||
return True
|
||
|
||
return False
|
||
explanation: |
|
||
Try starting from each cell. Use DFS to match characters one by one.
|
||
Mark cells temporarily, then restore when backtracking.
|