题目描述
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
输入:s = “aab”
输出:1
解释:只需一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子串。
输入:s = “a”
输出:0
解题分析
1.递归尝试
定义函数 f(string str,int i), 返回的结果是字符串str 从下标i开始到字符串末尾的最小分割次数,那么主函数即f(str,0)。
我们以 aabaca 为例分析,该字符串长度 n = 6
我们要求的结果是f(0), 此时我们可以分析f(0) 有哪些可能性:
- 从0位置分割,str[0…0] 为a 是回文串,这种可能性结果为 1+ f(1)
- 从1位置分割,str[0…1] 为 aa 是回文串, 这种可能性结果为 1 + f(2)
- 从2位置分割,str[0…2]: aab 不是回文串,不符合要求
- 从 3 位置分割,str[0…3]: aaba 也不是回文串,不符合要求
- 。。。。
因此可以写出下面的伪代码:
// 返回 [i...n-1] 的 最小分割次数
int f(int i){
if(str[i...n-1] 是回文串){
return 0;
}
int res = INT_MAX;
for(int j = i;j<n-1;++j){
if(str[i..j] 是回文串){
res = min(res,1+f(j+1))
}
}
return res;
}
2.动态规划
由上述暴力递归得知,我们当前的结果 f(i) , 可能依赖后续的每一个结果, 因此,我们定义 dp[i] 表示str[i…n-1] 的最少分割次数。
自然的,可以得出状态转移方程:
if(str[i..j] 是回文串){
dp[i] = min(dp[i],dp[j+1]+1);
}
因此,求解dp 数组我们需要从后往前求,最后结果为 dp[0]。
此时我们还剩下最后一个问题,即判断 str[i…j] 是否是回文,这是一个典型的动态规划,我们定义 isH[i][j] 表示str[i][j] 是否回文,
则状态转移方程为:
if(str[i] == str[j]){
isH[i][j] = isH[i+1][j-1];
}
至此,完整的代码如下:
int minCut(string s) {
int n = s.size();
// 1. first judge str[l...r]
vector<vector<int>> isH(n,vector<int>(n,0));
for(int i=0;i<n-1;++i){
isH[i][i] = 1;
if(s[i] == s[i+1]){
isH[i][i+1] = 1;
}
}
isH[n-1][n-1] = 1;
for(int i=n-3;i>=0;i--){
for(int j= i+2;j<n;++j){
if(s[i] == s[j]){
isH[i][j] = isH[i+1][j-1];
}
}
}
// dp[i] result of str[i...n-1]
vector<int> dp(n,INT_MAX);
dp[n-1] = 0;
for(int i=n-2;i>=0;i--){
if(isH[i][n-1] != 1){
for(int j=i;j<n-1;++j){
if(isH[i][j]){ // 判断str[i..j] 是否回文
dp[i] = min(dp[i],dp[j+1]+1);
}
}
}else{
dp[i] = 0;
}
}
return dp[0];
}
提交 通过。
如果有改进的地方,还希望多多指教,谢谢。