79. Word Search
MediumDescription
給定一個 m x n 的字元網格
board和一個字串word,判斷 word 是否存在於網格中。單字可以由相鄰(水平或垂直)的格子中的字母構成,同一格不能重複使用。
Example:
Input: board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED” Output: true
Intuition#
核心思路:從每個格子出發,用 DFS + 回溯嘗試匹配 word 的每個字元,走過的格子標記避免重複使用。
- 每個格子都可能是起點,嘗試所有起點
- 從起點出發,往四個方向 DFS,每步匹配 word 的下一個字元
- 回溯:走過的格子要標記(避免重複使用),回來時恢復
Approaches#
1: DFS + 額外 visited 陣列#
- 概念: 用一個 boolean 矩陣記錄已走過的格子
- 時間複雜度:
O(m * n * 4^L)- L 為 word 長度 - 空間複雜度:
O(m * n)- visited 陣列 + 遞迴深度
class Solution {
fun exist(board: Array<CharArray>, word: String): Boolean {
val m = board.size
val n = board[0].size
val visited = Array(m) { BooleanArray(n) }
fun dfs(i: Int, j: Int, k: Int): Boolean {
if (k == word.length) return true
if (i < 0 || i >= m || j < 0 || j >= n) return false
if (visited[i][j] || board[i][j] != word[k]) return false
visited[i][j] = true
val found = dfs(i + 1, j, k + 1) || dfs(i - 1, j, k + 1) ||
dfs(i, j + 1, k + 1) || dfs(i, j - 1, k + 1)
visited[i][j] = false
return found
}
for (i in 0 until m) {
for (j in 0 until n) {
if (dfs(i, j, 0)) return true
}
}
return false
}
}⭐2: DFS + 原地標記(空間最佳化)#
- 概念: 不使用額外 visited 陣列,直接將走過的格子改為特殊字元(如 ‘#’),回溯時恢復原值
- 時間複雜度:
O(m * n * 4^L) - 空間複雜度:
O(L)- 僅遞迴深度
class Solution {
fun exist(board: Array<CharArray>, word: String): Boolean {
val m = board.size
val n = board[0].size
fun dfs(i: Int, j: Int, k: Int): Boolean {
if (k == word.length) return true
if (i < 0 || i >= m || j < 0 || j >= n) return false
if (board[i][j] != word[k]) return false
val temp = board[i][j]
board[i][j] = '#' // 標記已走過
val found = dfs(i + 1, j, k + 1) || dfs(i - 1, j, k + 1) ||
dfs(i, j + 1, k + 1) || dfs(i, j - 1, k + 1)
board[i][j] = temp // 回溯恢復
return found
}
for (i in 0 until m) {
for (j in 0 until n) {
if (dfs(i, j, 0)) return true
}
}
return false
}
}原地修改 board 在多執行緒環境下不安全。面試時可以提到這個 trade-off:省空間 vs 安全性。
🔑 Takeaways#
- Pattern: 網格 DFS + 回溯是矩陣搜尋問題的標準模板
- 關鍵技巧: 原地標記(改成特殊字元再恢復)省去 visited 陣列;提前判斷不匹配可以大幅剪枝