力扣第132题-分割回文串 II

447 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

前言

力扣第132题 分割回文串 II 如下所示:

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。

返回符合要求的 最少分割次数 。

示例 1:

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

示例 2:

输入: s = "a"
输出: 0

一、思路

这题与前面一题力扣第131题-分割回文串非常类似,不过这一题只需要获得最小的分割次数

两次错误的尝试

下面的思路都有一个前提:通过 动态规划 初始化任意两个位置间的子串是否为回文串

我刚开始想的是:既然要获取最小的分割次数,那我每次递归从字符串的最后开始,以保证每次分割的子串尽可能的长,从而让分割的次数最小。伪代码如下所示:

    private void dfs(int i, int len, int count){
        if (i >= len){
            ret = Math.min(ret, count);
            return;
        }
        // 从后向前遍历,找到下一个最长的回文串
        for (int j=len-1; j>=i; j--){
            if (flag[i][j]){
                dfs(j+1, len, count+1);
                break;
            }
        }
    }

后面我就提交代码了,发现测试用例 s = aaabaa 是无法通过的。因为 第一次从 0 开始取最长的回文子串,只能取到 aaa,就导致了后续只剩下了 baa,故最小的分割次数为 3

既然上面这种思路不行,那我就把 break 的逻辑去除了,以保证能够获取到最小的分割次数。事实证明我还是太年轻了。提交后又发现无法通过 s3 = "ababababababababababababcbabababababababababababa" 的测试用力,因为递归会导致时间超时。

动态规划

想要优化递归,不妨尝试一下 动态规划。我们假设 dp[n]0 ~ n 的最小分割次数。状态转移方程为:

  • 0 ~ n 为回文字符串,则 dp[n] == 0
  • 0 ~ n 不为回文字符串,则取所有满足 i ~ n 为回文串( 0<i<n)时,dp[n] = min(dp[i] + 1)。翻译一下就是:枚举当前最后一个回文子串,来确定最小分割次数

有一点在实现过程中需要特别注意:当 j ~ i 为回文串的时候,dp[i] = Math.min(dp[i], dp[j-1] + 1),其中 j - 1 表示上一个位置的最小分割次数

二、实现

实现代码

实现代码与思路中保持一致

public int minCut(String s) {
    int len = s.length();
    boolean[][] flag = new boolean[len][len];
    for (int i = len - 1; i >= 0; i--)
    {
        for (int j = i; j < len; ++j)
        {
            if (i == j) // 对角线
                flag[i][j] = true;
            else if (j == i + 1)
                flag[i][j] = s.charAt(i) == s.charAt(j);
            else
                flag[i][j] = s.charAt(i) == s.charAt(j) && flag[i + 1][j - 1];//s[i]是否等于s[j] && 小区间是否为回文串
        }
    }
    int[] dp = new int[len];
    for (int i = 0; i<len; i++){
        if (flag[0][i]){
            dp[i] = 0;
        } else {
            dp[i] = len;
            // 枚举
            for (int j=1; j<=i; j++){
                if (flag[j][i]){
                    dp[i] = Math.min(dp[i], dp[j-1] + 1);
                }
            }
        }
    }
    return dp[len-1];
}

测试代码

public static void main(String[] args) {
    String s = "aab";
    String s1 = "ab";
    String s2 = "aaabaa";
    String s3 = "ababababababababababababcbabababababababababababa";
    int ret = new Number132().minCut(s);
    System.out.println(ret);
}

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~