携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
题目
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
示例 1
输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例 2
输入:s = "a"
输出:0
示例 3
输入:s = "ab"
输出:1
提示
1 <= s.length <= 2000s仅由小写英文字母组成
题解
思路
先对字符串进行预处理,isPal[j][i] 表示从字符 s[j] 到 s[i] 的子串是否回文,预处理分两种情况:
- 若子串字符在两个以内且俩字符相等,则该子串回文;
- 若子串的头尾两个字符相等,且中间部分回文,则该子串回文;
预处理完毕后,开始 DP,dp[i] 表示从字符串开头到第 i 个字符的最少回文分割次数,需要两重循环:
- 第一重循环:判断 s[0 ~ i] 子串本身是否为回文子串,若是,则最少分割次数为0;
- 第二重循环:计算状态转移方程,若 s[j ~ i] 是回文,则只需要在 s[j - 1] 和 s[j] 之间再做一次分割即可,即 dp[i] 为所有符合条件的j对应的 dp[j - 1] 的最小值加1。
代码
class Solution {
public:
int minCut(string s) {
int len = s.length();
vector<vector<bool>> isPal(len, vector<bool>(len));
for (int i = 0; i < len; ++ i)
{
for (int j = 0; j <= i; j ++ )
{
char ch1 = s[i], ch2 = s[j];
//i <= j + 1 表示从s[j]到s[i]在两个字符以内
if (ch1 == ch2 && (i <= j + 1 || isPal[j + 1][i - 1]))
{
isPal[j][i] = true;
}
}
}
vector<int> dp(len);
for (int i = 0; i < len; ++ i)
{
if (isPal[0][i])
{
dp[i] = 0; // s[0 ~ i] 本身就是回文子串,最少回文分割为 0
}
else
{
dp[i] = i; // s[0 ~ i] 的最大回文分割次数为 i
for (int j = 1; j <= i; ++ j)
{
if (isPal[j][i]) //若 s[j ~ i]为回文
{ //只需要在s[j - 1]和s[j]之间再做一次分割即可
dp[i] = min(dp[i], dp[j - 1] + 1);
}
}
}
}
return dp[len - 1];
}
};
结语
业精于勤,荒于嬉;行成于思,毁于随。