Welcome to Subscribe On Youtube
2911. Minimum Changes to Make K Semi-palindromes
Description
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
lenis considered a semi-palindrome if there exists a positive integerdsuch that1 <= d < lenandlen % 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 <= 2001 <= k <= s.length / 2sconsists only of lowercase English letters.
Solutions
Solution 1
Optimization: Preprocessing Divisors List
We can calculate the divisors list for each length, which can save a lot of division operations in the inner loop.
Solution 2
This implementation uses dynamic programming, followed by sliding window. It traverses the relevant values and updates its state as each value is processed. Previously computed states are retained so the same subproblem does not need to be solved again. After all required states have been considered, the maintained result is returned.
-
class Solution { public int minimumChanges(String s, int k) { int n = s.length(); int[][] g = new int[n + 1][n + 1]; int[][] f = new int[n + 1][k + 1]; final int inf = 1 << 30; for (int i = 0; i <= n; ++i) { Arrays.fill(g[i], inf); Arrays.fill(f[i], inf); } for (int i = 1; i <= n; ++i) { for (int j = i; j <= n; ++j) { int m = j - i + 1; for (int d = 1; d < m; ++d) { if (m % d == 0) { int cnt = 0; for (int l = 0; l < m; ++l) { int r = (m / d - 1 - l / d) * d + l % d; if (l >= r) { break; } if (s.charAt(i - 1 + l) != s.charAt(i - 1 + r)) { ++cnt; } } g[i][j] = Math.min(g[i][j], cnt); } } } } f[0][0] = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= k; ++j) { for (int h = 0; h < i - 1; ++h) { f[i][j] = Math.min(f[i][j], f[h][j - 1] + g[h + 1][i]); } } } return f[n][k]; } } // Solution 2 class Solution { static int inf = 200; List<Integer>[] factorLists; int n; int k; char[] ch; Integer[][] cost; public int minimumChanges(String s, int k) { this.k = k; n = s.length(); ch = s.toCharArray(); factorLists = getFactorLists(n); cost = new Integer[n + 1][n + 1]; return calcDP(); } static List<Integer>[] getFactorLists(int n) { List<Integer>[] l = new ArrayList[n + 1]; for (int i = 1; i <= n; i++) { l[i] = new ArrayList<>(); l[i].add(1); } for (int factor = 2; factor < n; factor++) { for (int num = factor + factor; num <= n; num += factor) { l[num].add(factor); } } return l; } int calcDP() { int[] dp = new int[n]; for (int i = n - k * 2 + 1; i >= 1; i--) { dp[i] = getCost(0, i); } int bound = 0; for (int subs = 2; subs <= k; subs++) { bound = subs * 2; for (int i = n - 1 - k * 2 + subs * 2; i >= bound - 1; i--) { dp[i] = inf; for (int prev = bound - 3; prev < i - 1; prev++) { dp[i] = Math.min(dp[i], dp[prev] + getCost(prev + 1, i)); } } } return dp[n - 1]; } int getCost(int l, int r) { if (l >= r) { return inf; } if (cost[l][r] != null) { return cost[l][r]; } cost[l][r] = inf; for (int factor : factorLists[r - l + 1]) { cost[l][r] = Math.min(cost[l][r], getStepwiseCost(l, r, factor)); } return cost[l][r]; } int getStepwiseCost(int l, int r, int stepsize) { if (l >= r) { return 0; } int left = 0; int right = 0; int count = 0; for (int i = 0; i < stepsize; i++) { left = l + i; right = r - stepsize + 1 + i; while (left + stepsize <= right) { if (ch[left] != ch[right]) { count++; } left += stepsize; right -= stepsize; } } return count; } } -
class Solution { public: int minimumChanges(string s, int k) { int n = s.size(); int g[n + 1][n + 1]; int f[n + 1][k + 1]; memset(g, 0x3f, sizeof(g)); memset(f, 0x3f, sizeof(f)); f[0][0] = 0; for (int i = 1; i <= n; ++i) { for (int j = i; j <= n; ++j) { int m = j - i + 1; for (int d = 1; d < m; ++d) { if (m % d == 0) { int cnt = 0; for (int l = 0; l < m; ++l) { int r = (m / d - 1 - l / d) * d + l % d; if (l >= r) { break; } if (s[i - 1 + l] != s[i - 1 + r]) { ++cnt; } } g[i][j] = min(g[i][j], cnt); } } } } for (int i = 1; i <= n; ++i) { for (int j = 1; j <= k; ++j) { for (int h = 0; h < i - 1; ++h) { f[i][j] = min(f[i][j], f[h][j - 1] + g[h + 1][i]); } } } return f[n][k]; } }; -
class Solution: def minimumChanges(self, s: str, k: int) -> int: n = len(s) g = [[inf] * (n + 1) for _ in range(n + 1)] for i in range(1, n + 1): for j in range(i, n + 1): m = j - i + 1 for d in range(1, m): if m % d == 0: cnt = 0 for l in range(m): r = (m // d - 1 - l // d) * d + l % d if l >= r: break if s[i - 1 + l] != s[i - 1 + r]: cnt += 1 g[i][j] = min(g[i][j], cnt) f = [[inf] * (k + 1) for _ in range(n + 1)] f[0][0] = 0 for i in range(1, n + 1): for j in range(1, k + 1): for h in range(i - 1): f[i][j] = min(f[i][j], f[h][j - 1] + g[h + 1][i]) return f[n][k] -
func minimumChanges(s string, k int) int { n := len(s) g := make([][]int, n+1) f := make([][]int, n+1) const inf int = 1 << 30 for i := range g { g[i] = make([]int, n+1) f[i] = make([]int, k+1) for j := range g[i] { g[i][j] = inf } for j := range f[i] { f[i][j] = inf } } f[0][0] = 0 for i := 1; i <= n; i++ { for j := i; j <= n; j++ { m := j - i + 1 for d := 1; d < m; d++ { if m%d == 0 { cnt := 0 for l := 0; l < m; l++ { r := (m/d-1-l/d)*d + l%d if l >= r { break } if s[i-1+l] != s[i-1+r] { cnt++ } } g[i][j] = min(g[i][j], cnt) } } } } for i := 1; i <= n; i++ { for j := 1; j <= k; j++ { for h := 0; h < i-1; h++ { f[i][j] = min(f[i][j], f[h][j-1]+g[h+1][i]) } } } return f[n][k] } -
function minimumChanges(s: string, k: number): number { const n = s.length; const g = Array.from({ length: n + 1 }, () => Array.from({ length: n + 1 }, () => Infinity)); const f = Array.from({ length: n + 1 }, () => Array.from({ length: k + 1 }, () => Infinity)); f[0][0] = 0; for (let i = 1; i <= n; ++i) { for (let j = 1; j <= n; ++j) { const m = j - i + 1; for (let d = 1; d < m; ++d) { if (m % d === 0) { let cnt = 0; for (let l = 0; l < m; ++l) { const r = (((m / d) | 0) - 1 - ((l / d) | 0)) * d + (l % d); if (l >= r) { break; } if (s[i - 1 + l] !== s[i - 1 + r]) { ++cnt; } } g[i][j] = Math.min(g[i][j], cnt); } } } } for (let i = 1; i <= n; ++i) { for (let j = 1; j <= k; ++j) { for (let h = 0; h < i - 1; ++h) { f[i][j] = Math.min(f[i][j], f[h][j - 1] + g[h + 1][i]); } } } return f[n][k]; } -
class Solution { static int inf = 200; List<Integer>[] factorLists; int n; int k; char[] ch; Integer[][] cost; public int minimumChanges(String s, int k) { this.k = k; n = s.length(); ch = s.toCharArray(); factorLists = getFactorLists(n); cost = new Integer[n + 1][n + 1]; return calcDP(); } static List<Integer>[] getFactorLists(int n) { List<Integer>[] l = new ArrayList[n + 1]; for (int i = 1; i <= n; i++) { l[i] = new ArrayList<>(); l[i].add(1); } for (int factor = 2; factor < n; factor++) { for (int num = factor + factor; num <= n; num += factor) { l[num].add(factor); } } return l; } int calcDP() { int[] dp = new int[n]; for (int i = n - k * 2 + 1; i >= 1; i--) { dp[i] = getCost(0, i); } int bound = 0; for (int subs = 2; subs <= k; subs++) { bound = subs * 2; for (int i = n - 1 - k * 2 + subs * 2; i >= bound - 1; i--) { dp[i] = inf; for (int prev = bound - 3; prev < i - 1; prev++) { dp[i] = Math.min(dp[i], dp[prev] + getCost(prev + 1, i)); } } } return dp[n - 1]; } int getCost(int l, int r) { if (l >= r) { return inf; } if (cost[l][r] != null) { return cost[l][r]; } cost[l][r] = inf; for (int factor : factorLists[r - l + 1]) { cost[l][r] = Math.min(cost[l][r], getStepwiseCost(l, r, factor)); } return cost[l][r]; } int getStepwiseCost(int l, int r, int stepsize) { if (l >= r) { return 0; } int left = 0; int right = 0; int count = 0; for (int i = 0; i < stepsize; i++) { left = l + i; right = r - stepsize + 1 + i; while (left + stepsize <= right) { if (ch[left] != ch[right]) { count++; } left += stepsize; right -= stepsize; } } return count; } }