488 祖玛游戏(记忆化搜索)

1. 问题描述:

回忆一下祖玛游戏。现在桌上有一串球,颜色有红色(R),黄色(Y),蓝色(B),绿色(G),还有白色(W)。 现在你手里也有几个球。
每一次,你可以从手里的球选一个,然后把这个球插入到一串球中的某个位置上(包括最左端,最右端)。接着,如果有出现三个或者三个以上颜色相同的球相连的话,就把它们移除掉。重复这一步骤直到桌上所有的球都被移除。
找到插入并可以移除掉桌上所有球所需的最少的球数。如果不能移除桌上所有的球,输出 -1 。

示例 1:
输入:board = "WRRBBW", hand = "RB"
输出:-1
解释:WRRBBW -> WRR[R]BBW -> WBBW -> WBB[B]W -> WW

示例 2:
输入:board = "WWRRBBWW", hand = "WRBRW"
输出:2
解释:WWRRBBWW -> WWRR[R]BBWW -> WWBBWW -> WWBB[B]WW -> WWWW -> empty

示例 3:
输入:board = "G", hand = "GGGGG"
输出:2
解释:G -> G[G] -> GG[G] -> empty 

示例 4:
输入:board = "RBYYBBRRB", hand = "YRBGB"
输出:3
解释:RBYYBBRRB -> RBYY[Y]BBRRB -> RBBBRRB -> RRRB -> B -> B[B] -> BB[B] -> empty 
提示:
你可以假设桌上一开始的球中,不会有三个及三个以上颜色相同且连着的球。
1 <= board.length <= 16
1 <= hand.length <= 5
输入的两个字符串均为非空字符串,且只包含字符 'R','Y','B','G','W'。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zuma-game

2. 思路分析:

分析题目可以知道最容易想到的是递归的方法,我们可以使用递归搜索所有可能的消除方案,这样肯定是可以求解出答案的,而且数据规模不是特别大应该可以通过。我们可以尝试将手中的球插入到当前board状态中的任意一个位置,使用一个方法来消除掉当前得到的新的状态的长度大于等于3的连续的字符串,为了减少不必要的搜索,我们可以使用记忆型的递归,其中一个哈希表来记录当前的局面,当前的局面由两个状态决定,第一个是当前插入小球之后消除的状态,另外一个状态是当前手中剩余小球的状态,由这两个状态那么就可以唯一确定当前的局面,第二个哈希表来记录当前手中剩余的球的数目,这样方便根据当前的状态插入小球。

3. 代码如下:

import collections

class Solution:
    # 获取手中剩余的球的状态
    def get(self):
        s = ""
        for k, v in self.count.items():
            s += k
        return s

    # 循环删除长度大于等于3的连续子串
    def clean_up(self, s: str):
        t = s
        isChange = 1
        i = 0
        while isChange > 0:
            isChange = 0
            i = 0
            while i < len(s):
                j = i + 1
                while j < len(s) and s[j] == s[i]: j += 1
                if j - i >= 3:
                    isChange = 1
                    s = s[0:i] + s[j:]
                    break
                # i = j
                i += 1
        return s

    res = 6

    # board表示当前的状态, hand当前手中的剩下球对应的状态
    def dfs(self, board: str, hand: str):
        if self.dic[board + " " + hand] >= self.res: return
        for k, v in self.count.items():
            if v > 0:
                self.count[k] -= 1
                # 当前的球可以插到开始与结尾处
                for i in range(len(board) + 1):
                    # 插入小球之后进行循环消除长度大于等于3的连续的小球
                    s = self.clean_up(board[0:i] + k + board[i:])
                    t = s + " " + self.get()
                    # 当前状态之前不存在或者到达当前状态的步数更小那么更新
                    if self.dic[t] == 0 or self.dic[t] > self.dic[board + " " + hand] + 1:
                        self.dic[t] = self.dic[board + " " + hand] + 1
                        if not s:
                            self.res = min(self.res, self.dic[t])
                        self.dfs(s, self.get())
                self.count[k] += 1

    # 字典dic来记录到达的状态, count记录当前手中球的数目
    dic, count = None, None

    def findMinStep(self, board: str, hand: str) -> int:
        self.dic, self.count = collections.defaultdict(int), collections.defaultdict(int)
        for c in hand:
            self.count[c] += 1
        self.dic[board] = 0
        self.dfs(board, self.get())
        # 最多操作5次所以当发现答案是6的时候那么返回-1
        if self.res == 6: return -1
        return self.res

版权声明:本文为qq_39445165原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。