解题报告-1278. Palindrome Partitioning III
题目链接:
https://leetcode.com/contest/weekly-contest-165/problems/palindrome-partitioning-iii/
解题思路:
题目要求是求出最小的值, 潜意识里最小,最大,最多这种最值问题大概率会和动态规划联系在一起.
因此直接建模dp[i][j][k]
i - 表示起点
j - 表示终点
k - 表示分成几个部分
dp[1][n][k] 就表示从起点1 到终点n 的字符串, 如果拆分成K个部分的最小代价是多少
这里使用1开头, n结尾, 而不是0开头和n-1结尾, 完全是因为个人觉得以0开头, 会搞的思路很乱, 所以我是直接用1了.
继续
dp[i][j][k] = min {
dp[i][j-1][k-1] + 从j 到 j 的计算代价
dp[i][j-2][k-1] + 从 j - 1 到 j 的计算代价,
dp[i][j-3][k-1] + 从 j - 2 到 j 的计算代价,
...
}
然后我们发现递推式中, 针对本题, i 永远都是起点, 所以可以把它省略掉. 所以递推公式变为
dp[j][k] = min {
dp[j-1][k-1] + 从j 到 j 的计算代价
dp[j-2][k-1] + 从 j - 1 到 j 的计算代价,
dp[j-3][k-1] + 从 j - 2 到 j 的计算代价,
...
}
那么从a到b的代价如何计算?
利用左右两个左边逐个比较, 相等代价为0, 不相等为1
递推式有了, 下一步需要定义初始值
我们画一个二维数组, 可以发现
dp[n][k] 的取值依赖前一列的值 dp[i][k-1], 并且在n行之上的元素
那么结合本题, 如果我们可以计算出第一列的值, 那么后续的列既可以求出
但是光求出列还不够, 因为当你是第一行的时候, 没有行在当前行之上了, 此时该怎么求?
所以我们直接把第一行的值求出来即可.
那么这里, 我们知道dp[i][j] 如果i < j 的话, 是不可能存在解的, 因为元素个数不够划分成j个部分
所以这部分的元素,我们应该给一个非常大的数, 这样我们在计算地推公式的时候, 就可以把这些数剔除掉
综上所述, 代码如下
class Solution {
//dp[i][j][k] 求最小值
//dp[1][n][k] = min(dp[1][n-1][k-1] + cost1, dp[1][n - 2][k -1] + cost2, dp[1][n - 3][k - 1] + cost3 ...)
//dp[n][k] = min(dp[n - 1][k - 1] + cost1, dp[n - 2][k - 1] + cost2, dp[n - 3][k - 1] + cost3, ...)
public int palindromePartition(String s, int k) {
int n = s.length();
int[][] dp = new int[n + 1][k + 1];
int bigVal = 1000000;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
dp[i][j] = bigVal;
}
}
for (int i = 1; i <= n; i++) {
dp[i][1] = calc(s, 0, i - 1);
}
for (int i = 2; i <= n; i++) {
for (int j = 2; j <= k; j++) {
for (int d = 1; d < i; d++) {
dp[i][j] = Math.min(dp[i][j], dp[d][j - 1] + calc(s, d + 1 - 1, i - 1));
}
}
}
return dp[n][k];
}
int calc(String s, int left, int right) {
int cost = 0;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
cost += 1;
}
left++;
right--;
}
return cost;
}
}