IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> LeetCode-132. 分割回文串 II -> 正文阅读

[数据结构与算法]LeetCode-132. 分割回文串 II

132. 分割回文串 II

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

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

示例 1:

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

示例 2:

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

分析阶段

动态规划

把字符串分割成多个子串,要求所有子串是回文串,返回最少的分割次数。最直接的做法是,把所有的子串都列举出来,从中找到最少的一种组合。题目 131. 分割回文串 就是找到所有的回文串组合,但是使用这种方法在 LeetCode 提交结果如下,需要使用新的解法:

image-20210807200228889

假设字符串 s s s 只有一种分割方法,如下所示:

s [ 0 , i 1 ] , s [ i 1 + 1 , i 2 ] , s [ i 2 + 1 , i 3 ] , . . . , s [ i k ? 1 , i k ] , s [ i k + 1 , n ? 1 ] s[0,i_1],s[i_1+1,i_2],s[i_2+1,i_3],...,s[i_{k-1},i_k],s[i_k+1,n-1] s[0,i1?],s[i1?+1,i2?],s[i2?+1,i3?],...,s[ik?1?,ik?],s[ik?+1,n?1],分割次数 k k k

可以看出,如果 s [ i k + 1 , n ? 1 ] s[i_k+1,n-1] s[ik?+1,n?1] 是回文串,那么 s [ 0 , n ? 1 ] s[0,n-1] s[0,n?1]的最少分割次数就是 “ s [ 0 , k ] s[0,k] s[0,k]的最少分割次数加1”。

如果字符串 s s s有多种分割方法,假设有三种方法,如下所示:

1、 s [ 0 , i 11 ] , s [ i 11 + 1 , i 1 2 ] , s [ i 12 + 1 , i 13 ] , . . . , s [ i k 1 ? 1 , i k 1 ] , s [ i k 1 + 1 , n ? 1 ] s[0,i_{11}],s[i_{11}+1,i_12],s[i_{12}+1,i_{13}],...,s[i_{k1-1},i_{k1}],s[i_{k1}+1,n-1] s[0,i11?],s[i11?+1,i1?2],s[i12?+1,i13?],...,s[ik1?1?,ik1?],s[ik1?+1,n?1],分割次数 k 1 k1 k1

2、 s [ 0 , i 21 ] , s [ i 21 + 1 , i 22 ] , s [ i 22 + 1 , i 23 ] , . . . , s [ i k 2 ? 1 , i k 2 ] , s [ i k 2 + 1 , n ? 1 ] s[0,i_{21}],s[i_{21}+1,i_{22}],s[i_{22}+1,i_{23}],...,s[i_{k2-1},i_{k2}],s[i_{k2}+1,n-1] s[0,i21?],s[i21?+1,i22?],s[i22?+1,i23?],...,s[ik2?1?,ik2?],s[ik2?+1,n?1],分割次数 k 2 k2 k2

3、 s [ 0 , i 31 ] , s [ i 31 + 1 , i 32 ] , s [ i 32 + 1 , i 33 ] , . . . , s [ i k 3 ? 1 , i k 3 ] , s [ i k 3 + 1 , n ? 1 ] s[0,i_{31}],s[i_{31}+1,i_{32}],s[i_{32}+1,i_{33}],...,s[i_{k3-1},i_{k3}],s[i_{k3}+1,n-1] s[0,i31?],s[i31?+1,i32?],s[i32?+1,i33?],...,s[ik3?1?,ik3?],s[ik3?+1,n?1],分割次数 k 3 k3 k3

要找到最少的分割次数,就是在 k 1 、 k 2 、 k 3 k1、k2、k3 k1k2k3 中找到最小值,而 k 1 、 k 2 、 k 3 k1、k2、k3 k1k2k3 分别是 s [ 0 , i k 1 ] 、 s [ 0 , i k 2 ] 、 s [ 0 , i k 3 ] s[0,i_{k1}]、s[0,i_{k2}]、s[0,i_{k3}] s[0,ik1?]s[0,ik2?]s[0,ik3?] 的最少分割次数加1。

因此,问题的子问题就为:在 s [ 0 , i k 1 ] 、 s [ 0 , i k 2 ] 、 s [ 0 , i k 3 ] s[0,i_{k1}]、s[0,i_{k2}]、s[0,i_{k3}] s[0,ik1?]s[0,ik2?]s[0,ik3?]的分割次数中找到最小值,并且 s [ 0 , i k 1 ] 、 s [ 0 , i k 2 ] 、 s [ 0 , i k 3 ] s[0,i_{k1}]、s[0,i_{k2}]、s[0,i_{k3}] s[0,ik1?]s[0,ik2?]s[0,ik3?]的分割次数也可以通过求解它们的子问题得到。

1、问题类型

第二类:对某种数据结构和算法的使用

使用的算法:动态规划

数据结构:构造状态数组

2、解题思路

从前面已经知道,在 s [ 0 , n ? 1 ] s[0,n-1] s[0,n?1] 范围内找到最少分割次数,就是要找到所有的回文子串下标 k ( k 1 、 k 2 、 k 3.... ) k(k1、k2、k3....) k(k1k2k3....) s [ 0 , n ? 1 ] s[0,n-1] s[0,n?1] 的最少分割次数等于$ s[0,k1]、s[0,k2]、s[0,k3]、… $ 中最小分割次数加1。

因此,我们可以构造一个数组,使用动态规划来求解最少分割次数,求解过程如下:

1、要求解的“状态”是:到字符串任意下标 i i i 处, s [ 0 , i ] s[0,i] s[0,i] 的最少的回文子串分割次数;

2、简化状态,得到“base case”: i = = 0 i == 0 i==0 或者 s [ 0 , i ] s[0,i] s[0,i] 本身就是回文串,分割次数为0;

3、构造“数组”:构造数组 d p [ n ] dp[n] dp[n]???, d p [ i ] dp[i] dp[i]??? 表示子串 s [ 0 , i ] s[0,i] s[0,i]??? 的最少分割次数;

4、构造状态转移方程:
d p [ i ] = { 0 , s [ 0 , i ] ?? 是回文子串 m i n { d p [ k ] } , k < i ?? a n d ?? s [ k , i ] ?? 是回文子串 dp[i]= \begin{cases} 0, & s[0,i]\; \text{是回文子串} \\[2ex] min\{dp[k]\}, & k < i\; and\; s[k,i]\;\text{是回文子串} \end{cases} dp[i]=????0,min{dp[k]},?s[0,i]是回文子串k<iands[k,i]是回文子串?
从“构造函数”可以看出,在确定 d p [ i ] dp[i] dp[i] 的值之前,要先知道所有 $dp[k](k \le i) $的值,所以在遍历字符串时,外层下标 i i i 从0开始遍历到 n n n,内层下标 k k k 从0开始遍历到 i i i。另外,我们要知道 s [ k , i ] s[k,i] s[k,i] 是否是回文子串,可以使用题目 5. 最长回文子串 构造的二维数组。

代码实现在“编码阶段”,在 LeetCode 的提交结果如下:

image-20210807200228889

编码阶段

class Solution {
    public int minCut(String s) {
        int n = s.length();
        boolean[][] isPalindrome = isPalindrome(s);
        int[] dp = new int[n];
        // 初始值
        Arrays.fill(dp, Integer.MAX_VALUE);

        for (int i = 0; i < n; i++) {
            if (isPalindrome[0][i]) {
                dp[i] = 0;
            } else {
                for (int k = 0; k < i; k++) {
                    if (isPalindrome[k + 1][i]) {
                        // 依次遍历下标 k,取出最小值
                        dp[i] = Math.min(dp[i], dp[k] + 1);
                    }
                }
            }
        }
        return dp[n - 1];
    }

    private boolean[][] isPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (i == j) {
                    dp[i][j] = true;
                } else {
                    boolean b = s.charAt(i) == s.charAt(j);
                    if (j == i + 1) {
                        dp[i][j] = b;
                    } else {
                        dp[i][j] = b & dp[i + 1][j - 1];
                    }
                }
            }
        }
        return dp;
    }
}

总结阶段

“分割回文串II”解题思路:

1、先用一个简单的例子,找到问题的子问题:最少次数是在多个回文子串中取最小值加1,再不断缩小子问题;

2、构造状态数组,直接保存状态: s [ 0 , i ] s[0,i] s[0,i] 的最少分割次数。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 13:36:58  更:2021-08-08 13:37:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:32:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码