Palindrome Partitioning II 解题心得
出处:https://leetcode.com/problems/palindrome-partitioning-ii/description/
题目复述
输入一个字符串,寻找将其切分为回文子段的最小切数
输出将输入字符串分为回文子串的最少分割点数
如:
输入
"aab"
输出
1
由于"aab"
可以只需要一个分割点分解为"aa", "b"
两个回文串
解题思路
- 开始的时候考虑动态转移方程
minC(d): 表示Str(0:d)之间的子串可以分为子字符串的最小分割点数
则minC(d) = min{minC(k)+1}, (0<k<d, if Str(k+1:d)是回文串)
- 发现线性遍历判断回文的时候的时间复杂度可能很高可能有
O(n3)
,于是考虑能不能先dp计算
Str(k+1:d)
是否为子串,则时间复杂度可以降低为 O(n2) - 所以问题集中在如何结合两个DP算法过程,使得算法效率最高;我们考虑能不能在两层循环的过中同时计算两个DP值
- 先写个判断回文的DP,因为这个我们最熟悉
vector<vector<bool>> isP(n, vector<bool>(n, false));
for (int i = 0; i < n; i++) isP[i][i] = true;
for (int j = 0; j < n; j++) {
for (int i = j-1; i >= 0; i--) {
if (s[i] == s[j] && (j-i == 1 || isP[i+1][j-1])) {
isP[i][j] = true;
}
}
}
这里就需要考虑如何调整i,j循环的方向,可能需要画图理解,因为其实解决简单的回文问题是有两种循环方向的一个是上面这种一种是下面这种,
...
//第二种
for (int j = n-1; j >= 0; j--) {
for (int i = j+1; i < n; i++) {
...
实际应用都能得到解,但是为了契合最小分割点数的dp,我们考虑使用上面第一种,这样每一层子循环才能边判断是否回文边利用已经计算过的isP
来判断比较哪一个最小分割点数的前续状态,即minC(d) = min{minC(k)+1}, (0<k<d, if Str(k+1:d)
这里的minC(k)
- 代码实现如下
class Solution {
public:
int minCut(string s) {
int n = s.size();
if (n<2) return 0;
vector<vector<bool>> isP(n, vector<bool>(n, false));
vector<int> minCut(n, INT_MAX-1);
minCut[0] = 0;
for (int i = 0; i < n; i++) isP[i][i] = true;
for (int j = 0; j < n; j++) {
if (j != 0) minCut[j] = min(minCut[j], minCut[j-1]+1);
for (int i = j-1; i >= 0; i--) {
if (s[i] == s[j] && (j-i == 1 || isP[i+1][j-1])) {
isP[i][j] = true;
int pre = i-1 >= 0 ? minCut[i-1] : -1;
minCut[j] = min(minCut[j], pre+1);
}
}
}
return minCut[n-1];
}
};
相关仓库:https://github.com/zhanzongyuan/leetcode/blob/master/132_Palindrome%20Partitioning%20II.cpp