题目描述
给你一个由小写字母组成的字符串 s
,和一个整数 k
。
请你按下面的要求分割字符串:
- 首先,你可以将
s
中的部分字符修改为其他的小写英文字母。 - 接着,你需要把
s
分割成k
个非空且不相交的子串,并且每个子串都是回文串。
请返回以这种方式分割字符串所需修改的最少字符数。
样例
输入:s = "abc", k = 2
输出:1
解释:你可以把字符串分割成 "ab" 和 "c",并修改 "ab" 中的 1 个字符,将它变成回文串。
输入:s = "aabbc", k = 3
输出:0
解释:你可以把字符串分割成 "aa"、"bb" 和 "c",它们都是回文串。
输入:s = "leetcode", k = 8
输出:0
限制
1 <= k <= s.length <= 100
s
中只含有小写英文字母。
算法
(动态规划) $O(n^3)$
- 首先预处理每个区间
[i, j]
变成回文所需要修改的字符数量(暴力计算即可),记为 $g(i, j)$。 - 设 $f(i, j)$ 表示前 $i$ 个字符,分为 $j$ 段最少所需要修改的字符数量,有效字符的下标从 1 开始。
- 初始时 $f(i, j)$ 为正无穷,$f(0, 0) = 0$。
- 转移时,枚举上一次的分隔点 $l$,从 0 到 $i - 1$,这一次转移所产生的新区间为
[l + 1, i]
,即 $f(i, j) = \min(f(l, j - 1) + g(l + 1, j)$。 - 最终答案为 $f(n, k)$。
时间复杂度
- 预处理需要 $O(n^3)$ 的时间。
- 动态规划状态数为 $O(nk)$ 个,每次转移需要 $O(n)$ 的时间。
- 故总时间复杂度为 $O(n^3)$。
空间复杂度
- 需要额外 $O(n^2)$ 的空间记录数组 $g$。
- 动态规划的状态需要 $O(nk)$ 的空间记录。
- 总空间复杂度为 $O(n^2)$。
C++ 代码
class Solution {
public:
int calc(string t) {
int tot = 0;
for (int i = 0, j = t.length() - 1; i < j; i++, j--)
if (t[i] != t[j])
tot++;
return tot;
}
int palindromePartition(string s, int k) {
const int MAX = 10000;
int n = s.length();
vector<vector<int>> f(n + 1, vector<int>(k + 1, MAX));
vector<vector<int>> g(n + 1, vector<int>(n + 1, MAX));
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
g[i][j] = calc(s.substr(i - 1, j - i + 1));
f[0][0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= min(n, k); j++)
for (int l = 0; l < i; l++)
f[i][j] = min(f[i][j], f[l][j - 1] + g[l + 1][i]);
return f[n][k];
}
};
递归实现
优化了一下OP的实现, 4ms beat 99%. 不开O2