leetcode - [132]分割回文串‖|刷题打卡

218 阅读3分钟

1. 题目描述

[132] 分割回文串‖
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。

示例:
输入: "aab"
输出: 1
解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。

Related Topics 动态规划

2. 思路分析

​ 首先,根据131题目的思路的思路,得到一个辅助计算的 二维dp表 ​ 然后,可以将切分的次数状态理解为, 如果无法完全切割,则为截止至上一个字符的字符串的最小切割次数 + 1(f[n] = f[n-1] + 1

​ 在这中间,可能还有不同的切分方案,因此,需要比较不同的切分方案来确定最小的值

3. AC代码

public class No132 {
    public int minCut(String s) {
        return method1(s);
//        return method2(s);
    }

    /**
     * https://leetcode-cn.com/problems/palindrome-partitioning-ii/solution/xiang-jie-liang-bian-dong-tai-gui-hua-ji-s5xr/
     * 执行耗时:10 ms,击败了85.25% 的Java用户
     * 内存消耗:38.1 MB,击败了73.61% 的Java用户
     * @param s
     * @return
     */
    private int method2(String s) {
        int n = s.length();
        char[] cs = s.toCharArray();

        // 预处理出 st,st[i][j] 表示区间 [i,j] 是否为回文串
        boolean[][] st = new boolean[n][n];
        for (int j = 0; j < n; j++) {
            for (int i = j; i >= 0; i--) {
                // 当 [i, j] 只有一个字符时,必然是回文串
                if (i == j) {
                    st[i][j] = true;
                } else {
                    // 当 [i, j] 长度为 2 时,满足 cs[i] == cs[j] 即回文串
                    if (j - i + 1 == 2) {
                        st[i][j] = cs[i] == cs[j];

                        // 当 [i, j] 长度大于 2 时,满足 (cs[i] == cs[j] && f[i + 1][j - 1]) 即回文串
                    } else {
                        st[i][j] = cs[i] == cs[j] && st[i + 1][j - 1];
                    }
                }
            }
        }

        // f(i) 代表考虑下标为 i 的字符为结尾的最小分割次数
        int[] f = new int[n];
        for (int j = 1; j < n; j++) {

            // 如果 [0,j] 这一段直接构成回文,则无须分割
            if (st[0][j]) {
                f[j] = 0;

                // 如果无法直接构成回文
                // 那么对于第 j 个字符,有使用分割次数,或者不使用分割次数两种选择
            } else {
                // 下边两种决策也能够合到一个循环当中去做,但是需要先将 f[i] 预设为一个足够大的数,因此干脆拆开来做

                // 独立使用一次分割次数
                f[j] = f[j - 1] + 1;

                // 第 j 个字符本身不独立使用分割次数
                // 代表要与前面的某个位置 i 形成区间 [i,j],使得 [i, j] 形成回文,[i, j] 整体消耗一次分割次数
                for (int i = 1; i < j; i++) {
                    if (st[i][j]) {
                        f[j] = Math.min(f[j], f[i - 1] + 1);
                    }
                }
            }
        }
        return f[n - 1];
    }

    /**
     * 一次动态规划不行,就两次
     *
     * 执行耗时:16 ms,击败了62.38% 的Java用户
     * 内存消耗:38.1 MB,击败了74.51% 的Java用户
     * @param s
     * @return
     */
    private int method1(String s) {
        boolean dp[][] = new boolean[s.length()][s.length()];

        // 构造 dp表,方便后续的判断比较
        for (int j = 0; j < s.length(); j++) {
            for (int i = 0; i < j; i++) {
                if(i==j) {
                    dp[i][j] = true;
                } else {
                    if( j - i < 3) {
                        dp[i][j] = s.charAt(i) == s.charAt(j);
                    } else {
                        dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i+1][j-1];
                    }
                }
            }
        }

        // 标记从 下标从0到这个位置的字符串 的切割次数
        int[] positionCut = new int[s.length()];
        for (int j = 1; j < s.length(); j++) {
            if(dp[0][j]) {
                positionCut[j] = 0;
            } else {
                positionCut[j] = positionCut[j-1] + 1;

                // 查看j点之前是否有更佳的切分点,次数更少,找出当前位置的最优切分次数
                for (int i = 1; i < j; i++) {
                    if(dp[i][j]) {
                        positionCut[j] = Math.min(positionCut[j],positionCut[i-1] + 1);
                    }
                }
            }
        }

        return positionCut[s.length()-1];
    }
}

4. 总结

​ 可以先看看leetcode - [131]分割回文串 的method2

​ 一个指针不能够完全解决的问题的时候,就用两个指针。一次动态规划不能解决的问题的时候,就用两次


​ 本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情