一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情
题目(Palindrome Partitioning II)
链接:https://leetcode-cn.com/problems/palindrome-partitioning-ii
解决数:500
通过率:49.4%
标签:字符串 动态规划
相关公司:google amazon bytedance
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
示例 1:
输入: s = "aab"
输出: 1
解释: 只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2:
输入: s = "a"
输出: 0
示例 3:
输入: s = "ab"
输出: 1
提示:
1 <= s.length <= 2000s仅由小写英文字母组成
思路
看到这种求极值的问题应该不难想到dp
:索引 0 到 i 的子串 的最小分割数
题目求: , n 为字符串 s 的长度
base case 是什么?如果 就是回文串,不用切割,此时
对应的子串长度为 ,最多能被分割 次 所以我们初始化 ,包含了 base case:
找出状态转移方程
我们尝试将子问题拆成规模小一点的子问题,找到它们之间的联系。
表示 的最小分割数,我们用指针 j 去切分一下 ,切一个规模小一点的 dp 子问题出来。
分成了两部分: 和 ,其中 的最小分割数是 ,它相对于 是计算过的状态,我们要找出 和 的递推关系。
对于 ,如果它是回文串,就有递推关系:
因为 j 指针是在扫 ,j 在变,它切的 如果多次是回文串, 取最小的 就好
两次 DP
因为我们需要判断 子串是否回文,就用到昨天的131题的 dp 解法:
用的 dp 二维数组存放每个 子串是否回文。所以这道题用了两次 dp,但两次都不复杂。
代码
func minCut(s string) int {
n := len(s)
isPali := make([][]bool, n) // isPali[i][j] 表示 [i,j] 子串是否回文
for i := range isPali {
isPali[i] = make([]bool, n)
}
// 计算isPali矩阵
for j := 0; j < n; j++ { // 我采用从左上开始的扫描方向,也可以选别的
for i := 0; i <= j; i++ {
if i == j { // 长度为1的子串 本身就是回文
isPali[i][j] = true
} else if j-i == 1 && s[i] == s[j] { // 长度为2的子串 两个字符需相同
isPali[i][j] = true
} else if j-i > 1 && s[i] == s[j] && isPali[i+1][j-1] {//剩余子串也回文
isPali[i][j] = true
}
}
}
// 第二次dp
dp := make([]int, n) // dp[i] 表示 [0,i] 子串的最小分割次数
for i := 0; i < n; i++ { // 初始化dp
dp[i] = i
}
for i := 1; i < n; i++ { // 遍历计算 dp[i]
if isPali[0][i] { // 如果[0,i]就是回文,不用分割,最小分割次数为0
dp[i] = 0
continue
}
for j := 0; j < i; j++ { // 用指针j去划分[0,i]
if isPali[j+1][i] { // 如果[j+1,i]是回文 则有dp[i]=dp[j]+1
if dp[j] + 1 < dp[i] {
dp[i] = dp[j] + 1 // 遍历结束时 dp[i]就是最小值了
}
}
}
}
return dp[n-1] // 从0到len(s)-1的字符串s的最小分割次数
}
var minCut = function (s) {
const n = s.length
const isPali = new Array(n);
for (let i = 0; i < n; i++) {
isPali[i] = new Array(n);
}
for (let j = 0; j < n; j++) {
for (let i = 0; i <= j; i++) {
if (i == j) {
isPali[i][j] = true
} else if (j - i == 1 && s[i] == s[j]) {
isPali[i][j] = true
} else if (j-i > 1 && s[i] == s[j] && isPali[i+1][j-1]) {
isPali[i][j] = true
} else {
isPali[i][j] = false
}
}
}
const dp = new Array(n);
for (let i = 0; i < n; i++) {
dp[i] = i;
}
for (let i = 0; i < n; i++) {
if (isPali[0][i]) {
dp[i] = 0;
continue
}
for (let j = 0; j < i; j++) {
if (isPali[j + 1][i]) {
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[n - 1];
};