Palindrome Partitioning III
HardUpdated: Aug 2, 2025
Practice on:
Palindrome Partitioning 3
Problem
You are given a string s containing lowercase letters and an integer k. You need to :
- First, change some characters of
sto other lowercase English letters. - Then divide
sintoknon-empty disjoint substrings such that each substring is a palindrome.
Return the minimal number of characters that you need to change to divide the string.
Examples
Example 1:
Input:
s = "abc", k = 2
Output:
1
Explanation: You can split the string into "ab" and "c", and change 1 character in "ab" to make it palindrome.
Example 2:
Input:
s = "aabbc", k = 3
Output:
0
Explanation: You can split the string into "aa", "bb" and "c", all of them are palindrome.
Example 3:
Input:
s = "leetcode", k = 8
Output:
0
Solution
Method 1 – DP with Palindrome Cost Preprocessing
Intuition
We first compute the minimum number of changes needed to make any substring a palindrome. Then, we use DP to partition the string into k palindromic substrings with minimal total changes.
Approach
- Precompute
cost[i][j]: the minimal changes to makes[i..j]a palindrome. - Let
dp[i][k]be the minimal changes to partitions[i:]intokpalindromic substrings. - For each partition, try all possible next cuts and take the minimum.
Code
C++
class Solution {
public:
int palindromePartition(string s, int K) {
int n = s.size();
vector<vector<int>> cost(n, vector<int>(n));
for (int i = 0; i < n; ++i)
for (int j = i; j < n; ++j) {
int l = i, r = j, cnt = 0;
while (l < r) if (s[l++] != s[r--]) ++cnt;
cost[i][j] = cnt;
}
vector<vector<int>> dp(n+1, vector<int>(K+1, 1e9));
dp[n][0] = 0;
for (int i = n-1; i >= 0; --i)
for (int k = 1; k <= K; ++k)
for (int j = i; j < n; ++j)
dp[i][k] = min(dp[i][k], cost[i][j] + dp[j+1][k-1]);
return dp[0][K];
}
};
Go
func palindromePartition(s string, K int) int {
n := len(s)
cost := make([][]int, n)
for i := range cost { cost[i] = make([]int, n) }
for i := 0; i < n; i++ {
for j := i; j < n; j++ {
l, r, cnt := i, j, 0
for l < r {
if s[l] != s[r] { cnt++ }
l++; r--
}
cost[i][j] = cnt
}
}
dp := make([][]int, n+1)
for i := range dp { dp[i] = make([]int, K+1); for k := range dp[i] { dp[i][k] = 1e9 } }
dp[n][0] = 0
for i := n-1; i >= 0; i-- {
for k := 1; k <= K; k++ {
for j := i; j < n; j++ {
if cost[i][j]+dp[j+1][k-1] < dp[i][k] {
dp[i][k] = cost[i][j]+dp[j+1][k-1]
}
}
}
}
return dp[0][K]
}
Java
class Solution {
public int palindromePartition(String s, int K) {
int n = s.length();
int[][] cost = new int[n][n];
for (int i = 0; i < n; ++i)
for (int j = i; j < n; ++j) {
int l = i, r = j, cnt = 0;
while (l < r) if (s.charAt(l++) != s.charAt(r--)) ++cnt;
cost[i][j] = cnt;
}
int[][] dp = new int[n+1][K+1];
for (int[] row : dp) Arrays.fill(row, 1000000000);
dp[n][0] = 0;
for (int i = n-1; i >= 0; --i)
for (int k = 1; k <= K; ++k)
for (int j = i; j < n; ++j)
dp[i][k] = Math.min(dp[i][k], cost[i][j] + dp[j+1][k-1]);
return dp[0][K];
}
}
Kotlin
class Solution {
fun palindromePartition(s: String, K: Int): Int {
val n = s.length
val cost = Array(n) { IntArray(n) }
for (i in 0 until n)
for (j in i until n) {
var l = i; var r = j; var cnt = 0
while (l < r) { if (s[l++] != s[r--]) cnt++ }
cost[i][j] = cnt
}
val dp = Array(n+1) { IntArray(K+1) { 1_000_000_000 } }
dp[n][0] = 0
for (i in n-1 downTo 0)
for (k in 1..K)
for (j in i until n)
dp[i][k] = minOf(dp[i][k], cost[i][j] + dp[j+1][k-1])
return dp[0][K]
}
}
Python
class Solution:
def palindromePartition(self, s: str, K: int) -> int:
n = len(s)
cost = [[0]*n for _ in range(n)]
for i in range(n):
for j in range(i, n):
l, r, cnt = i, j, 0
while l < r:
if s[l] != s[r]: cnt += 1
l += 1; r -= 1
cost[i][j] = cnt
dp = [[float('inf')] * (K+1) for _ in range(n+1)]
dp[n][0] = 0
for i in range(n-1, -1, -1):
for k in range(1, K+1):
for j in range(i, n):
dp[i][k] = min(dp[i][k], cost[i][j] + dp[j+1][k-1])
return dp[0][K]
Rust
impl Solution {
pub fn palindrome_partition(s: String, k: i32) -> i32 {
let n = s.len();
let s = s.as_bytes();
let mut cost = vec![vec![0; n]; n];
for i in 0..n {
for j in i..n {
let (mut l, mut r, mut cnt) = (i, j, 0);
while l < r {
if s[l] != s[r] { cnt += 1; }
l += 1; r -= 1;
}
cost[i][j] = cnt;
}
}
let k = k as usize;
let mut dp = vec![vec![1_000_000_000; k+1]; n+1];
dp[n][0] = 0;
for i in (0..n).rev() {
for kk in 1..=k {
for j in i..n {
dp[i][kk] = dp[i][kk].min(cost[i][j] + dp[j+1][kk-1]);
}
}
}
dp[0][k]
}
}
TypeScript
class Solution {
palindromePartition(s: string, K: number): number {
const n = s.length;
const cost: number[][] = Array.from({length: n}, () => Array(n).fill(0));
for (let i = 0; i < n; i++)
for (let j = i; j < n; j++) {
let l = i, r = j, cnt = 0;
while (l < r) {
if (s[l] !== s[r]) cnt++;
l++; r--;
}
cost[i][j] = cnt;
}
const dp: number[][] = Array.from({length: n+1}, () => Array(K+1).fill(Infinity));
dp[n][0] = 0;
for (let i = n-1; i >= 0; i--)
for (let k = 1; k <= K; k++)
for (let j = i; j < n; j++)
dp[i][k] = Math.min(dp[i][k], cost[i][j] + dp[j+1][k-1]);
return dp[0][K];
}
}
Complexity
- ⏰ Time complexity:
O(n^3)wherenis the length of the string (for cost and DP computation). - 🧺 Space complexity:
O(n^2)for the cost and DP tables.