得到 K 个半回文串的最少修改次数
难度:
标签:
题目描述
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.
代码结果
运行时间: 416 ms, 内存: 21.7 MB
// Problem: Similar to the above solution but using Java Streams for more concise code.
// Approach: We use a similar approach as above, utilizing streams to calculate the minimum changes needed for each substring.
// We loop through each possible division point and use streams to determine the required changes.
import java.util.*;
import java.util.stream.IntStream;
public class SolutionStream {
public int minChangesToSemiPalindrome(String s, int k) {
int n = s.length();
int[][] dp = new int[k + 1][n + 1];
for (int[] row : dp) Arrays.fill(row, Integer.MAX_VALUE / 2);
dp[0][0] = 0;
for (int i = 1; i <= k; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = IntStream.range(0, j)
.map(m -> dp[i - 1][m] + minChanges(s.substring(m, j)))
.min().orElse(Integer.MAX_VALUE / 2);
}
}
return dp[k][n];
}
private int minChanges(String str) {
int len = str.length();
return (int) IntStream.range(0, len / 2)
.filter(i -> str.charAt(i) != str.charAt(len - i - 1))
.count();
}
}
解释
方法:
该题解采用了动态规划和记忆化搜索的方法来解决问题。主要分为两部分:
1. **get_modify(start, end)**: 计算将子字符串 s[start:end+1] 转换成半回文串的最小修改次数。它通过枚举所有可能的 d 值来找到最优解。对于每个 d,它检查通过分组跳跃比较字符是否能形成多个小的回文字符串。
2. **dp(k, end)**: 使用动态规划来计算最终结果,dp(k, end) 表示将字符串 s[0:end+1] 分割成 k 个半回文子字符串的最小修改次数。它递归地尝试所有可能的分割点,结合 get_modify 函数结果来更新状态。
整体上,通过组合这两个函数的结果来构建全局最优解。
时间复杂度:
O(n^3 * K)
空间复杂度:
O(n^2 + n * K)
代码细节讲解
🦆
在函数`get_modify(start, end)`中,为什么将最大修改次数初始化为`length`?这是否意味着最坏情况下每个字符都需要修改?
▷🦆
在枚举所有可能的`d`值时,为什么选择跳过`length % d != 0`的情况?这对于确定是否可以形成半回文串有什么特别的意义吗?
▷🦆
在计算每个可能的`d`时,具体是如何通过`分组跳跃比较字符`来检查是否能形成多个小的回文字符串的?能否详细解释这个过程?
▷🦆
在`dp(k, end)`函数中,为什么特别将`k==1`的情况单独处理,而不是统一使用循环逻辑来处理?
▷