力扣解题思路:488. 祖玛游戏

488. 祖玛游戏

思路:在这里插入图片描述
实际上就是简单的消消乐,如果时间允许,最简单的暴力递归法也是可以的,就是把所有字母插入所有的位置,取最短且可以消去的插入球数即可。但是这样无脑插入是很浪费时间的,所以我们在插入的时候应该先选好合适的位置,分一下两种情况:
(1)插入一个或两个颜色相同的球引发连锁反响。
(2)往两个颜色相同的球中间插入一个颜色不同的球(为什么要这么做呢?见特殊测试用例二)。

注意两个特殊的测试用例:

测试用例一:
"WWRRGGRRWWRRGGRRWW", "GG"
无论怎么插入,都无法完全消除,结果应是-1。

测试用例二:
"RRWWRRBBRR", "WB"
"R(B)RWWRRBBRR" -> "R(B)RWW(W)RRBBRR" -> ""
结果应是2

首先我们需要一个方法来用于消去字符串,返回消去后的结果:

    private StringBuilder eliminate(StringBuilder sb) {
        boolean flag = true;
        while (flag) {
            flag = false;
            for (int i = 0; i < sb.length(); i++) {
                int j = i + 1;
                while (j < sb.length() && sb.charAt(j) == sb.charAt(i)) {
                    j++;
                }
                if (j - i >= 3) {
                    sb.delete(i, j);
                    flag = true;
                }
            }
        }
        return sb;
    }

我们使用Map来保存可用字符的数量,用数组或者hashMap都可以,这里为了简洁我们使用数组:

    private int result = Integer.MAX_VALUE;
    private int[] map = new int[26];
    private char[] colors = {'R', 'Y', 'B', 'G', 'W'};
    public int findMinStep(String board, String hand) {
        for (int i = 0; i < hand.length(); i++) {
            map[hand.charAt(i) - 'A']++;
        }
        dfs(new StringBuilder(board), 0);
        return result == Integer.MAX_VALUE ? -1 : result;
    }

接下来就是递归方法的编写,我们先定义递归的出口:
1.到达递归终点(目标字符串已被全部消除)
2.目前已用的字符个数比之前result中的更大(因此不必继续递归)

        if (step >= result) return;
        if (board.length() == 0) {
            result = Math.min(step, result);
            return;
        }

接下来就可以分情况讨论:
(1)插入一个或两个颜色相同的球引发连锁反响。
(2)往两个颜色相同的球中间插入一个颜色不同的球。

for (int i = 0; i < board.length(); i++) {
            char c = board.charAt(i);
            int j = i;
            while (j + 1 < board.length() && board.charAt(j + 1) == c) {
                j++;
            }//统计同色球个数
            if (j == i && map[c - 'A'] >= 2) {  //只有单个球
                StringBuilder tmp = new StringBuilder(board);
                tmp.insert(i, c + "" + c);
                map[c - 'A'] -= 2;
                dfs(eliminate(tmp), step + 2);
                map[c - 'A'] += 2;
            } else if (j == i + 1) {    //存在两个颜色相同且相邻的球
                if (map[c - 'A'] >= 1) {
                    StringBuilder tmp = new StringBuilder(board);
                    tmp.insert(i, c);
                    map[c - 'A']--;
                    dfs(eliminate(tmp), step + 1);
                    map[c - 'A']++;
                }
                for (char color : colors) {
                    if (color == c) {
                        continue;
                    }
                    if (map[color - 'A'] >= 1) {
                        StringBuilder tmp = new StringBuilder(board);
                        tmp.insert(i + 1, color);   //尝试往这两个颜色相同且相邻的球中间插入一个颜色不同的球
                        map[color - 'A']--;
                        dfs(eliminate(tmp), step + 1);
                        map[color - 'A']++;
                    }
                }
            }

完整代码如下:

    private int result = Integer.MAX_VALUE;
    private int[] map = new int[26];
    private char[] colors = {'R', 'Y', 'B', 'G', 'W'};
    public int findMinStep(String board, String hand) {
        for (int i = 0; i < hand.length(); i++) {
            map[hand.charAt(i) - 'A']++;
        }
        dfs(new StringBuilder(board), 0);
        return result == Integer.MAX_VALUE ? -1 : result;
    }
    private void dfs(StringBuilder board, int step) {
        if (step >= result) return;
        if (board.length() == 0) {
            result = Math.min(step, result);
            return;
        }
        for (int i = 0; i < board.length(); i++) {
            char c = board.charAt(i);
            int j = i;
            while (j + 1 < board.length() && board.charAt(j + 1) == c) {
                j++;
            }
            if (j == i && map[c - 'A'] >= 2) {  //只有单个球
                StringBuilder tmp = new StringBuilder(board);
                tmp.insert(i, c + "" + c);
                map[c - 'A'] -= 2;
                dfs(eliminate(tmp), step + 2);
                map[c - 'A'] += 2;
            } else if (j == i + 1) {    //存在两个颜色相同且相邻的球
                if (map[c - 'A'] >= 1) {
                    StringBuilder tmp = new StringBuilder(board);
                    tmp.insert(i, c);
                    map[c - 'A']--;
                    dfs(eliminate(tmp), step + 1);
                    map[c - 'A']++;
                }
                for (char color : colors) {
                    if (color == c) {
                        continue;
                    }
                    if (map[color - 'A'] >= 1) {
                        StringBuilder tmp = new StringBuilder(board);
                        tmp.insert(i + 1, color);   //尝试往这两个颜色相同且相邻的球中间插入一个颜色不同的球
                        map[color - 'A']--;
                        dfs(eliminate(tmp), step + 1);
                        map[color - 'A']++;
                    }
                }
            }
        }
    }
    private StringBuilder eliminate(StringBuilder sb) {
        boolean flag = true;
        while (flag) {
            flag = false;
            for (int i = 0; i < sb.length(); i++) {
                int j = i + 1;
                while (j < sb.length() && sb.charAt(j) == sb.charAt(i)) {
                    j++;
                }
                if (j - i >= 3) {
                    sb.delete(i, j);
                    flag = true;
                }
            }
        }
        return sb;
    }