Given a string s and an integer k, partition s into ksubstrings such that the letter changes needed to make each substring a semi-palindrome are minimized.
Return the minimum number of letter changes required .
A semi-palindrome is a special type of string that can be divided into
palindromes based on a repeating pattern. To check if a string is a semi-palindrome:
Choose a positive divisor d of the string’s length. d can range from 1 up to, but not including, the string’s length. For a string of length 1, it does not have a valid divisor as per this definition, since the only divisor is its length, which is not allowed.
For a given divisor d, divide the string into groups where each group contains characters from the string that follow a repeating pattern of length d. Specifically, the first group consists of characters at positions 1, 1 + d, 1 + 2d, and so on; the second group includes characters at positions 2, 2 + d, 2 + 2d, etc.
The string is considered a semi-palindrome if each of these groups forms a palindrome.
Consider the string "abcabc":
The length of "abcabc" is 6. Valid divisors are 1, 2, and 3.
For d = 1: The entire string "abcabc" forms one group. Not a palindrome.
For d = 2:
Group 1 (positions 1, 3, 5): "acb"
Group 2 (positions 2, 4, 6): "bac"
Neither group forms a palindrome.
For d = 3:
Group 1 (positions 1, 4): "aa"
Group 2 (positions 2, 5): "bb"
Group 3 (positions 3, 6): "cc"
All groups form palindromes. Therefore, "abcabc" is a semi-palindrome.
Input: s ="abcac", k =2Output: 1Explanation: Divide `s` into `"ab"` and `"cac"`.`"cac"`is already semi-palindrome. Change `"ab"` to `"aa"`, it becomes semi-palindrome with`d = 1`.
To minimize changes, we need to split the string into k substrings and, for each, find the minimum changes to make it a semi-palindrome. Precompute the cost for every substring to become a semi-palindrome, then use dynamic programming to find the optimal partitioning.
classSolution {
public:int minChanges(string s, int k) {
int n = s.size();
vector<vector<int>> cost(n, vector<int>(n, 0));
for (int l =0; l < n; ++l) {
for (int r = l; r < n; ++r) {
int len = r - l +1, minCost = INT_MAX;
for (int d =1; d < len; ++d) {
if (len % d !=0) continue;
int cur =0;
for (int i =0; i < d; ++i) {
int left = i, right = len - d + i;
while (left < right) {
if (s[l + left] != s[l + right]) cur++;
left += d;
right -= d;
}
}
minCost = min(minCost, cur);
}
cost[l][r] = (len ==1?0: minCost);
}
}
vector<vector<int>> dp(n +1, vector<int>(k +1, INT_MAX));
dp[0][0] =0;
for (int i =1; i <= n; ++i) {
for (int j =1; j <= k; ++j) {
for (int p = j -1; p < i; ++p) {
if (dp[p][j -1] != INT_MAX && cost[p][i -1] != INT_MAX)
dp[i][j] = min(dp[i][j], dp[p][j -1] + cost[p][i -1]);
}
}
}
return dp[n][k];
}
};
classSolution {
publicintminChanges(String s, int k) {
int n = s.length();
int[][] cost =newint[n][n];
for (int l = 0; l < n; ++l) {
for (int r = l; r < n; ++r) {
int len = r - l + 1, minCost = Integer.MAX_VALUE;
for (int d = 1; d < len; ++d) {
if (len % d != 0) continue;
int cur = 0;
for (int i = 0; i < d; ++i) {
int left = i, right = len - d + i;
while (left < right) {
if (s.charAt(l + left) != s.charAt(l + right)) cur++;
left += d;
right -= d;
}
}
minCost = Math.min(minCost, cur);
}
cost[l][r]= (len == 1 ? 0 : minCost);
}
}
int[][] dp =newint[n + 1][k + 1];
for (int[] arr : dp) Arrays.fill(arr, Integer.MAX_VALUE);
dp[0][0]= 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
for (int p = j - 1; p < i; ++p) {
if (dp[p][j - 1]!= Integer.MAX_VALUE&& cost[p][i - 1]!= Integer.MAX_VALUE)
dp[i][j]= Math.min(dp[i][j], dp[p][j - 1]+ cost[p][i - 1]);
}
}
}
return dp[n][k];
}
}
classSolution {
funminChanges(s: String, k: Int): Int {
val n = s.length
val cost = Array(n) { IntArray(n) }
for (l in0 until n) {
for (r in l until n) {
val len = r - l + 1var minCost = Int.MAX_VALUE
for (d in1 until len) {
if (len % d !=0) continuevar cur = 0for (i in0 until d) {
var left = i
var right = len - d + i
while (left < right) {
if (s[l + left] != s[l + right]) cur++ left += d
right -= d
}
}
minCost = minOf(minCost, cur)
}
cost[l][r] = if (len ==1) 0else minCost
}
}
val dp = Array(n + 1) { IntArray(k + 1) { Int.MAX_VALUE } }
dp[0][0] = 0for (i in1..n) {
for (j in1..k) {
for (p in j - 1 until i) {
if (dp[p][j - 1] !=Int.MAX_VALUE && cost[p][i - 1] !=Int.MAX_VALUE)
dp[i][j] = minOf(dp[i][j], dp[p][j - 1] + cost[p][i - 1])
}
}
}
return dp[n][k]
}
}
defmin_changes(s: str, k: int) -> int:
n = len(s)
cost = [[0] * n for _ in range(n)]
for l in range(n):
for r in range(l, n):
length = r - l +1 min_cost = float('inf')
for d in range(1, length):
if length % d !=0:
continue cur =0for i in range(d):
left, right = i, length - d + i
while left < right:
if s[l + left] != s[l + right]:
cur +=1 left += d
right -= d
min_cost = min(min_cost, cur)
cost[l][r] =0if length ==1else min_cost
dp = [[float('inf')] * (k +1) for _ in range(n +1)]
dp[0][0] =0for i in range(1, n +1):
for j in range(1, k +1):
for p in range(j -1, i):
if dp[p][j -1] != float('inf') and cost[p][i -1] != float('inf'):
dp[i][j] = min(dp[i][j], dp[p][j -1] + cost[p][i -1])
return dp[n][k]
impl Solution {
pubfnmin_changes(s: String, k: i32) -> i32 {
let n = s.len();
let s = s.as_bytes();
letmut cost =vec![vec![0; n]; n];
for l in0..n {
for r in l..n {
let len = r - l +1;
letmut min_cost =i32::MAX;
for d in1..len {
if len % d !=0 { continue; }
letmut cur =0;
for i in0..d {
letmut left = i;
letmut right = len - d + i;
while left < right {
if s[l + left] != s[l + right] { cur +=1; }
left += d;
right -= d;
}
}
min_cost = min_cost.min(cur);
}
cost[l][r] =if len ==1 { 0 } else { min_cost };
}
}
letmut dp =vec![vec![i32::MAX; (k +1) asusize]; n +1];
dp[0][0] =0;
for i in1..=n {
for j in1..=k asusize {
for p in j -1..i {
if dp[p][j -1] !=i32::MAX&& cost[p][i -1] !=i32::MAX {
dp[i][j] = dp[i][j].min(dp[p][j -1] + cost[p][i -1]);
}
}
}
}
dp[n][k asusize]
}
}