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

LeetCode 488: Zuma Game

·2 分鐘· ·
LeetCode Hard Dynamic-Programming Memoization Breadth-First-Search String Stack
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-07-08

  • 提交紀錄:Wrong Answer ×2 → Accepted

解題思路
#

插入一顆球之後會連鎖消除,沒辦法拆成獨立的區間。但 board 最多 16 顆、hand 最多 5 顆,可以把 (board, hand) 整個當成狀態,DFS + 記憶化:

  • board 空了:0 步。
  • 對 hand 裡每一種球(排序後跳過相同的)、board 的每個插入位置,插入後消除,遞迴,取最小值 + 1。

剪枝:只在兩種位置插入,其他位置不會更好:

  1. 插在同色球的前面:湊成一組。
  2. 把兩顆相同、但跟手上這顆不同色的球拆開:例如 RR 中間插 W 變成 RWR,之後兩邊的 R 可以跟其他 R 湊。

消除用一個迴圈:找到長度 >= 3 的連續同色就刪掉,從頭重新檢查,直到沒有可以刪的。

我的嘗試
#

版本問題
第一版只在同色球旁邊插入,漏掉「拆開兩顆同色球」的走法
第二版加上拆開的條件,但插入的位置寫成 b[:j + 1] + h[i] + b[j + 1:],插在 b[j] 的後面;拆開兩顆同色球要插在 b[j - 1] 和 b[j] 之間,結果還是沒拆開
AC改成 b[:j] + h[i] + b[j:]

兩版都在 board = "RRWWRRBBRR"、hand = "WB" 失敗:答案是 2(先在 RR 中間插 W 拆開,觸發連鎖消除),兩版都回傳 -1。

正確解法
#

from functools import cache

class Solution:
    def findMinStep(self, board: str, hand: str) -> int:
        def clean(s: str) -> str:
            i = 0
            while i < len(s):
                j = i
                while j < len(s) and s[j] == s[i]:
                    j += 1
                if j - i >= 3:
                    s = s[:i] + s[j:]              # 刪掉之後從頭再檢查(連鎖消除)
                    i = 0
                else:
                    i = j
            return s

        @cache
        def dfs(b: str, h: str) -> float:
            if not b:
                return 0
            best = float("inf")
            for i in range(len(h)):
                if i > 0 and h[i] == h[i - 1]:
                    continue                       # 同一種球只試一次
                for j in range(len(b) + 1):
                    same = j < len(b) and b[j] == h[i]
                    split = 0 < j < len(b) and b[j - 1] == b[j] != h[i]
                    if same or split:
                        nb = clean(b[:j] + h[i] + b[j:])
                        best = min(best, 1 + dfs(nb, h[:i] + h[i + 1:]))
            return best

        res = dfs(board, "".join(sorted(hand)))
        return -1 if res == float("inf") else res

複雜度
#

狀態數是 board 經過若干次插入消除的結果 × hand 的子集,最壞是指數級,但 board 和 hand 都很短,加上記憶化和剪枝可以過。

要特別避免的錯誤
#

  • 只在同色球旁邊插入:「拆開兩顆同色球」有時候是唯一的解法。
  • 插入位置差一格。
  • hand 要先排序,才能跳過相同的球。

面試口說整理
#

插入之後會連鎖消除,沒辦法拆成子問題,但 board 和 hand 都很短,所以我把整個 (board, hand) 當狀態做記憶化 DFS。每一步試 hand 裡每種球、每個插入位置,只在同色球前面或拆開兩顆同色球的地方插入,插入後模擬消除再遞迴,取最小步數。

相關文章

LeetCode 1745: Palindrome Partitioning IV
·1 分鐘
LeetCode Hard Dynamic-Programming Interval-Dp String
LeetCode 1745 解題筆記:先用區間 DP 建好 is_pal[i][j],再枚舉兩個切點,三段都是回文就成立,O(n^2)
LeetCode 3816: Lexicographically Smallest String After Deleting Duplicate Characters
·1 分鐘
LeetCode Hard Stack Monotonic-Stack Greedy String
LeetCode 3816 解題筆記:單調棧,頂端比較大而且還有別的副本就刪;最後把尾巴重複的也刪掉,因為前綴一定比較小
LeetCode 42: Trapping Rain Water
·1 分鐘
LeetCode Hard Array Two-Pointers Dynamic-Programming Stack
LeetCode 42 解題筆記:每格能存的水是 min(左邊最高, 右邊最高) - 自己的高度;用前後綴最大值或左右雙指針
LeetCode 1147: Longest Chunked Palindrome Decomposition
·1 分鐘
LeetCode Hard Dynamic-Programming Greedy Two-Pointers Rolling-Hash String
LeetCode 1147 解題筆記:從兩端同時往內累積字串,左右一相等就切成一對;越早切越好,剩下的中間(如果有)算一塊
LeetCode 84: Largest Rectangle in Histogram
·1 分鐘
LeetCode Daily Hard Array Stack Monotonic-Stack Dynamic-Programming Geometry Histogram
使用單調堆疊解決直方圖中最大矩形面積問題
LeetCode 1411: Number of Ways to Paint N × 3 Grid
·1 分鐘
LeetCode Hard Dynamic-Programming State-Machine Graph-Coloring
LeetCode 1411 解題筆記:一列的塗法只分兩種樣式:ABA(兩端同色)和 ABC(三色不同),各 6 種;下一列 ABA → 3 ABA + 2 ABC、ABC → 2 ABA + 2 ABC