Minimum Changes to Make K Semi-palindromes
Minimum Changes to Make K Semi-palindromes
Given a string s
and an integer k
, partition s
into k
substrings such that the sum of the number of letter changes required to turn each substring into a semi-palindrome is minimized.
Return an integer denoting the minimum number of letter changes required.
Notes
- A string is a palindrome if it can be read the same way from left to right and right to left.
- A string with a length of
len
is considered a semi-palindrome if there exists a positive integerd
such that1 <= d < len
andlen % d == 0
, and if we take indices that have the same modulo byd
, they form a palindrome. For example,"aa"
,"aba"
,"adbgad"
, and,"abab"
are semi-palindrome and"a"
,"ab"
, and,"abca"
are not. - A substring is a contiguous sequence of characters within a string.
Example 1:
Input: s = "abcac", k = 2
Output: 1
Explanation: We can divide s into substrings "ab" and "cac". The string "cac" is already a semi-palindrome. If we change "ab" to "aa", it becomes a semi-palindrome with d = 1.
It can be shown that there is no way to divide the string "abcac" into two semi-palindrome substrings. Therefore, the answer would be at least 1.
Example 2:
Input: s = "abcdef", k = 2
Output: 2
Explanation: We can divide it into substrings "abc" and "def". Each of the substrings "abc" and "def" requires one change to become a semi-palindrome, so we need 2 changes in total to make all substrings semi-palindrome.
It can be shown that we cannot divide the given string into two substrings in a way that it would require less than 2 changes.
Example 3:
Input: s = "aabbaa", k = 3
Output: 0
Explanation: We can divide it into substrings "aa", "bb" and "aa".
The strings "aa" and "bb" are already semi-palindromes. Thus, the answer is zero.
Constraints:
2 <= s.length <= 200
1 <= k <= s.length / 2
s
consists only of lowercase English letters.
解题思路
比赛的时候被傻逼第三题卡了快一个小时,导致没多少时间写这题,事实上这题比第三题简单多了,不过代码调了蛮久,而且有个特别坑的地方,就是长度为 的子串不是半回文串!因为给出了 的条件。补题的时候被这个地方卡了半天,md绝了。
首先一眼 dp,定义 表示将前 个字符划分为 个半回文子串的所有方案中修改次数的最小值,根据最后一段子串(以 为右端点的子串)的左端点 进行状态划分,因此有状态转移方程 。
其中 是指将 的子串 变成半回文串所需要的最小修改次数。直接暴力算就好了,分别枚举左右端点 和 ,然后对子串的长度 分解约数 ,如果 ,那么提取子串中相应的位置(模 相同的下标),计算把这些位置构成的串变成回文串的最小修改次数(若两端的字符不一样就要对其中一个修改一次)。对所有满足条件的 计算修改次数,取最小值,就是 的值。
我们知道求 中每个数的约数可以用类似线性筛的做法求,时间复杂度是 ,意味着平均下来 每个数大概有 个约数。因此上述做法的时间复杂度是 。如果一开始把每个数的约数都预处理出来,那么时间复杂度就优化到 。
dp 的时间复杂度是 ,因此总的时间复杂度就是 。
AC 代码如下:
class Solution {
public:
int minimumChanges(string s, int m) {
int n = s.size();
vector<vector<int>> p(n + 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j * j <= i; j++) {
if (i % j == 0) {
p[i].push_back(j);
if (j > 1 && i / j != j) p[i].push_back(i / j);
}
}
}
vector<vector<int>> g(n + 1, vector<int>(n + 1, 0x3f3f3f3f));
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
function<int(int)> get = [&](int x) {
int cnt = 0;
for (int u = 0; u < x; u++) {
string t;
for (int v = i + u; v <= j; v += x) {
t += s[v - 1];
}
for (int l = 0, r = t.size() - 1; l < r; l++, r--) {
if (t[l] != t[r]) cnt++;
}
}
return cnt;
};
for (auto &x : p[j - i + 1]) {
g[i][j] = min(g[i][j], get(x));
}
}
}
vector<vector<int>> f(n + 1, vector<int>(m + 1, 0x3f3f3f3f));
f[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 1; k < i; k++) {
f[i][j] = min(f[i][j], f[k - 1][j - 1] + g[k][i]);
}
}
}
return f[n][m];
}
};
参考资料
第 368 场力扣周赛:https://leetcode.cn/circle/discuss/haZ1d7/
划分型 DP【力扣周赛 368】:https://www.bilibili.com/video/BV12w411B7ia/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17785091.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-10-24 C1. Make Nonzero Sum (easy version)
2022-10-24 D1. Balance (Easy version)
2022-10-24 B. Ugu