题目
题目来源:力扣(LeetCode)
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
示例 1:
输入:s = “aab”
输出:1
解释:只需一次分割就可将 s 分割成 [“aa”,”b”] 这样两个回文子串。
示例 2:
输入:s = “a”
输出:0
示例 3:
输入:s = “ab”
输出:1
思路分析
使用两次动态规划解决这道问题。
第一次 dp 使用二维数组判断 区间范围[i, j] 的子串是否是回文字符串
第二次 dp 根据第一次 dp 的预处理结果求取 范围是[0, i]的回文子串的最少分割次数
/**
* @param {string} s
* @return {number}
*/
var minCut = function (s) {
const n = s.length;
const g = new Array(n).fill(0).map(() => new Array(n).fill(true));
// 第一次动态规划 数据预处理 求出字符串 所有子串是否为回文串 true/false
// g[i][j] 代表 [i, j] 这一段是否为回文
// g[i][j] 是否回文,必须满足以下两个条件:
// g[i+1][j−1]=true
// s[i] == s[j]
// g[i][j] 是否回文,是依赖于 g[i+1][j−1] 的,只有 g[i+1][j−1] 为true时,才能赋值 g[i][j] 为true
// 在 g[i][j] 二维数组中,g[i+1][j−1]是在 g[i][j] 的左下角的
// 为了保证 g[i + 1][j - 1]都是经过计算的,一定要先从下到上遍历行,然后从左到右遍历遍历列
for (let i = n - 1; i >= 0; --i) {
for (let j = i + 1; j < n; ++j) {
g[i][j] = s[i] == s[j] && g[i + 1][j - 1];
}
}
const dp = new Array(n).fill(Number.MAX_SAFE_INTEGER);
// 第二次动态规划 dp[i]为 0 ~ i分割回文串的最小分割次数
// 如果 0 ~ i 本身为回文串 则分割次数为0
for (let i = 0; i < n; ++i) {
if (g[0][i]) {
dp[i] = 0;
} else {
for (let j = 0; j < i; ++j) {
// 如果j + 1 到 i 是回文串
if (g[j + 1][i]) {
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
}
return dp[n - 1];
};
参考阅读: https://leetcode-cn.com/problems/palindromic-substrings/solution/dai-ma-sui-xiang-lu-dai-ni-xue-tou-dpzi-vidge/ https://leetcode-cn.com/problems/palindrome-partitioning-ii/solution/132-fen-ge-hui-wen-chuan-iidong-tai-gui-3hqva/ https://leetcode-cn.com/problems/palindrome-partitioning-ii/solution/dong-tai-gui-hua-by-liweiwei1419-2/