↓快轉到主要內容
  1. LeetCode/

LeetCode 79: Word Search

·2 分鐘· ·
LeetCode Medium Array String Backtracking Depth-First-Search
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2022-10-26

  • 提交紀錄:Wrong Answer ×4 → Time Limit Exceeded → Accepted → Runtime Error ×2 → Wrong Answer → Runtime Error → Time Limit Exceeded ×2 → Accepted → Time Limit Exceeded → Runtime Error → Time Limit Exceeded → Runtime Error → Time Limit Exceeded → Runtime Error → Time Limit Exceeded → Accepted → Runtime Error ×2 → Time Limit Exceeded ×2 → Accepted → Runtime Error ×2 → Time Limit Exceeded → Runtime Error → Accepted → Time Limit Exceeded ×3 → Accepted ×2 → Time Limit Exceeded ×3 → Accepted ×3

解題思路
#

網格回溯(Backtracking 變化三):dfs(i, j, k) 檢查 board[i][j] == word[k],對到最後一個字元就成功;否則把這格暫時改成 # 防止重複使用,往四個方向找 k + 1,回來時還原。每一格都要當起點試。

剪枝很重要,不然大的測資會 TLE:

  • word 的某個字元次數比 board 裡多,直接 False。
  • 比較 word 頭尾字元在 board 裡的次數,從比較少的那一端開始搜尋(把 word 反轉),分支會少很多。

我的嘗試
#

這題提交了 42 次,是這次整理裡最多的:

  • 2022-10-26 第一版只從 (0, 0) 開始、四個方向裡 i + 1 寫了兩次而沒有 j + 1,標記之後也沒還原:[['c', 'a']] 找 'a' 回傳 False。
  • 方向和起點修好之後就一直 TLE。2022-10、2022-11 前後試了二十幾次,換了寫法(傳 word[1:] 的切片、用 visited 集合、加減 index),大部分都卡在 Python 太慢,中間也有幾次 AC。
  • 2026-10 重寫,最後一版加上了「字元次數不夠就直接 False」的剪枝。

正確解法
#

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        m, n = len(board), len(board[0])
        have = Counter(c for row in board for c in row)
        need = Counter(word)
        if any(have[c] < k for c, k in need.items()):
            return False
        if have[word[0]] > have[word[-1]]:
            word = word[::-1]
        def dfs(i, j, k):
            if board[i][j] != word[k]:
                return False
            if k == len(word) - 1:
                return True
            board[i][j] = "#"
            found = any(0 <= x < m and 0 <= y < n and dfs(x, y, k + 1)
                        for x, y in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)))
            board[i][j] = word[k]
            return found
        return any(dfs(i, j, 0) for i in range(m) for j in range(n))

複雜度
#

時間 O(mn · 3^L),空間 O(L)(遞迴深度)。

要特別避免的錯誤
#

  • 每一格都要當起點,不是只從 (0, 0) 開始。
  • 四個方向要寫對,不要重複寫同一個方向。
  • 標記之後要還原,不然其他起點會被影響。
  • 沒有剪枝的 Python 版本很容易 TLE。

面試口說整理
#

我從每一格開始回溯,對到目前的字元就把這格暫時標記,往四個方向找下一個字元,回來時還原。先用字元次數剪枝:board 不夠的字元直接回傳 False,並且從頭尾中比較稀有的那一端開始搜尋。最壞時間 O(mn · 3^L)。

相關文章

LeetCode 78: Subsets
·1 分鐘
LeetCode Medium Array Backtracking Bit-Manipulation
LeetCode 78 解題筆記:每個元素選或不選;迭代寫法是每加入一個數,就把現有的每個子集合複製一份並加上它
LeetCode 90: Subsets II
·1 分鐘
LeetCode Medium Array Backtracking Bit-Manipulation
LeetCode 90 解題筆記:先排序;遇到跟前一個相同的數,只延伸上一步新產生的那些子集合
LeetCode 47: Permutations II
·1 分鐘
LeetCode Medium Array Backtracking Sorting
LeetCode 47 解題筆記:先排序,同一層如果跟前一個值相同而且前一個還沒被用,就跳過
LeetCode 49: Group Anagrams
·1 分鐘
LeetCode Medium Array Hash-Table String Sorting
LeetCode 49 解題筆記:把每個字串排序後當成 key,同一個 key 的字串就是同一組 anagram
LeetCode 39: Combination Sum
·1 分鐘
LeetCode Medium Array Backtracking
LeetCode 39 解題筆記:同一個數可以重複選,所以遞迴時下一層從 i 開始(不是 i + 1),這樣也避免了順序不同的重複
LeetCode 40: Combination Sum II
·1 分鐘
LeetCode Medium Array Backtracking
LeetCode 40 解題筆記:每個數只能用一次而且有重複,排序後下一層從 i + 1 開始,同一層跳過跟前一個相同的值