Palindrome Partitioning II

Updated on 02 July, 2025
Palindrome Partitioning II header image

Problem Statement

Given a string s, the objective is to split the string into as few parts as possible such that each segment of the string is a palindrome. A palindrome is a sequence of characters which reads the same backward as forward, such as "radar" or "level". The task is to determine the minimum number of cuts needed to achieve this. For example, if s is "aab", it’s possible to split it into ["aa", "b"] with just a single cut, as "aa" and "b" are both palindromes. The challenge lies in planning these cuts to minimize their number while ensuring each resulting substring is a palindrome.

Examples

Example 1

Input:

s = "aab"

Output:

1

Explanation:

The palindrome partitioning ["aa","b"] could be produced using 1 cut.

Example 2

Input:

s = "a"

Output:

0

Example 3

Input:

s = "ab"

Output:

1

Constraints

  • 1 <= s.length <= 2000
  • s consists of lowercase English letters only.

Approach and Intuition

Given the underlying nature of the problem, a dynamic programming approach could be highly effective in solving this problem, where past computed results contribute to solutions for progressively larger subproblems. Here's a breakdown of the logical steps involved in approaching the problem:

  1. Understanding Simple Cases:

    • If the string s is already a palindrome, no cuts are needed.
    • If the string s has all distinct characters, then (length of s - 1) cuts would be the straightforward answer.
  2. Dynamic Programming Array Initiation:

    • Create an array dp[] where dp[i] represents the minimal cuts needed to partition the substring s[0:i] into palindromic substrings.
    • Initially set dp[i] to the maximum cuts possible, which could be (i-1) assuming no two characters are the same.
  3. Palindrome Check Array:

    • Alongside, maintain a 2D boolean matrix isPalindrome[][] where isPalindrome[i][j] == true if the substring s[i:j+1] is a palindrome. This matrix helps in reducing redundant calculations.
    • Update isPalindrome[i][j] based on the relation with isPalindrome[i+1][j-1], starting from checking single characters and expanding by comparing characters and checking the previously validated substrings.
  4. Dynamic Programming Calculation:

    • Iterate through the string characters.
    • For each character, if the substring ending in the current character is a palindrome, potentially update dp[i] as 0 since no cut is needed from the start.
    • If not, calculate the minimum cuts required by using previously computed dp[] values, considering every possible cut position that splits the substring into a palindrome and some other substring for which the cuts have already been optimally calculated.
  5. Utilization of Constraints:

    • The constraints suggest the approach should ideally be better than O(n^3) to be efficient within the given bounds.
    • The complexity primarily arises from filling up the boolean palindrome table and subsequently calculating the minimum cuts using this pre-computed table.

This combination of pre-computation and dynamic programming allows each decision to be made based on previously learned information about the string, optimizing the partitions needed and ensuring efficiency even for the upper limits of input size.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
```cpp
class Solution {
public:
    int minimumCuts(string s) {
        vector<int> dp;
        dp.resize(s.size());
        for (int i = 1; i < s.size(); i++) {
            dp[i] = i;
        }
        for (int mid = 0; mid < s.size(); mid++) {
            // Check for both odd and even length palindromes
            minimizeCuts(mid, mid, dp, s);
            minimizeCuts(mid - 1, mid, dp, s);
        }
        return dp[s.size() - 1];
    }
    
    void minimizeCuts(int left, int right, vector<int> &dp, string s) {
        for (int l = left, r = right; l >= 0 && r < s.size() && s[l] == s[r]; l--, r++) {
            int newCut = l == 0 ? 0 : dp[l - 1] + 1;
            dp[r] = min(dp[r], newCut);
        }
    }
};
    
The provided C++ code defines a solution to the problem of finding the minimum number of cuts required to partition a string into substrings, each of which is a palindrome.
    
* The function `minimumCuts` initializes a dynamic programming array `dp` where `dp[i]` represents the minimum number of cuts needed for the substring `s[0...i]`. Initially, it sets `dp[i]` to `i`, assuming the worst case where each character might be a separate palindrome.
* The function then iterates through each character in the string as a potential center (`mid`) for palindromes. It checks for palindromes of both odd and even lengths by calling the helper function `minimizeCuts`.
* The helper function `minimizeCuts` extends palindromes from the center outwards. If a palindrome is found, it calculates the minimum cuts required by comparing the current number of cuts with new possible cuts, updating `dp` accordingly.
    
The algorithm focuses on optimizing the partition by dynamically storing and updating results for substrings up to each character, finding the overall minimum cuts required for the whole string. The use of palindromic centers helps efficiently cater to both even and odd-length palindromes, enhancing the procedure's effectiveness.
java
class Solution {
    public int minimumCuts(String s) {
        int[] computedCuts;
        computedCuts = new int[s.length()];
        for (int j = 1; j < s.length(); j++) {
            computedCuts[j] = j;
        }
        for (int center = 0; center < s.length(); center++) {
            // Odd length palindromes
            calculateCuts(center, center, computedCuts, s);
            // Even length palindromes
            calculateCuts(center - 1, center, computedCuts, s);
        }
        return computedCuts[s.length() - 1];
    }
    
    public void calculateCuts(int left, int right, int[] computedCuts, String str) {
        for (
            int start = left, end = right;
            start >= 0 && end < str.length() && str.charAt(start) == str.charAt(end);
            start--, end++
        ) {
            int possibleNewCut = start == 0 ? 0 : computedCuts[start - 1] + 1;
            computedCuts[end] = Math.min(computedCuts[end], possibleNewCut);
        }
    }
}

The Java solution described aims to determine the minimum number of cuts required to partition a string such that each substring is a palindrome. The solution uses dynamic programming to achieve this efficiently. Here is a breakdown of the implemented approach:

  • Initialization:

    • Create an integer array computedCuts where each position j represents the minimum cuts needed for the substring from the start up to position j.
    • Initialize computedCuts[j] to j for all positions, as, in the worst case, each character could be a cut point.
  • Palindrome Check and Cut Calculation:

    • The function calculateCuts checks for palindromic substrings centered around each index center of the input string s:
      • It considers both odd and even length palindromes by setting two different initial configurations for start and end pointers: (center, center) for odd lengths and (center-1, center) for even lengths.
      • Inside calculateCuts, iterate outward from the center, checking for palindromic property using the conditions that characters at start and end should match and these pointers should not cross the boundary of the string.
      • If a palindrome is found, compute the minimum cuts needed by comparing the current number of cuts (computedCuts[end]) with the cuts that would result if this palindrome were considered as a new end point after the last cut (computedCuts[start-1] + 1).
  • Result:

    • After processing each center point in the string, the final minimum number of cuts required for the whole string is stored in computedCuts[s.length() - 1].

This method uses a 2D palindrome checking combined with dynamic programming for optimizing the cut placement, leveraging the property that palindromic cuts can optimize subsequent decisions in substring partitioning. This results in an efficient solution in terms of both time and space.

c
int calculateMinCuts(char* str) {
    int length = strlen(str);
    int* dpCuts = (int*)malloc(sizeof(int) * length);
    for (int i = 0; i < length; i++) {
        dpCuts[i] = i;
    }
    for (int center = 0; center < length; center++) {
        optimalCuts(center, center, dpCuts, str, length);
        optimalCuts(center - 1, center, dpCuts, str, length);
    }
    int minCuts = dpCuts[length - 1];
    free(dpCuts);
    return minCuts;
}
void optimalCuts(int left, int right, int* dpCuts, char* str,
                     int length) {
    for (int l = left, r = right;
         l >= 0 && r < length && str[l] == str[r]; l--, r++) {
        int cut = l == 0 ? 0 : dpCuts[l - 1] + 1;
        dpCuts[r] = dpCuts[r] < cut ? dpCuts[r] : cut;
    }
}

The given C code provides a solution for determining the minimum number of cuts required to partition a string such that each subsection of the string is a palindrome. The problem-solving approach utilizes dynamic programming.

  • Start by defining a function calculateMinCuts which calculates the minimum number of cuts for a string passed as a char* argument.
  • Initialize dynamic programming array dpCuts where each position i in dpCuts will store the minimum cuts needed for substring from start to i.
  • Set every index of dpCuts with its index value, assuming the worst case where each character might be a cut.
  • Utilize a helper function optimalCuts to update the dpCuts array values based on palindrome conditions around a central point.
  • Loop through each possible center position of palindromes in the string. For each center, consider both even-length and odd-length palindromes.
  • In optimalCuts, use a two-pointer technique to expand around centers and update the cuts dynamically if the entire segment from start (l) to end (r) is a palindrome.
  • If the segment is palindrome and starts at index zero, no cuts are needed; else, adjust dpCuts[r] by considering extra cut needed using previous cut values.
  • After determining all possible optimal cuts, the value at the final index length-1 of dpCuts will give the minimum cuts needed for the entire string.
  • Free up memory allocated for dpCuts and return the minimum number of cuts.

This algorithm efficiently calculates the minimum cuts by not only checking every substrings but also by expanding around possible palindrome centers and updating the dpCuts based on the palindromic properties of the substrings, thus ensuring optimal calculations for large inputs.

js
var palindromePartitioningMinCuts = function (str) {
    let minCuts = new Array(str.length).fill(0);
    for (let ind = 1; ind < str.length; ind++) {
        minCuts[ind] = ind;
    }
    for (let current = 0; current < str.length; current++) {
        optimalCut(current, current, minCuts, str);
        optimalCut(current - 1, current, minCuts, str);
    }
    return minCuts[str.length - 1];
};
function optimalCut(left, right, minCuts, str) {
    for (
        let leftIdx = left, rightIdx = right;
        leftIdx >= 0 && rightIdx < str.length && str.charAt(leftIdx) == str.charAt(rightIdx);
        leftIdx--, rightIdx++
    ) {
        let cutPosition = leftIdx == 0 ? 0 : minCuts[leftIdx - 1] + 1;
        minCuts[rightIdx] = Math.min(minCuts[rightIdx], cutPosition);
    }
}

The problem "Palindrome Partitioning II" involves finding the minimum number of cuts required to partition a given string such that every substring is a palindrome. The provided code implements this solution in JavaScript. Here is a succinct summary:

  • Initialize an array minCuts where minCuts[i] represents the minimum number of cuts required for the substring str[0...i].
  • Set each entry of minCuts with its respective index initially, since the maximum cuts needed would be slicing each character individually.
  • For each character in the string as a mid-point, expand outwards to validate palindrome sequences using two approaches: expanding from a single middle character and from a pair of characters. This is handled by the optimalCut function.
  • The optimalCut function checks for palindromes by expanding from the center outwards. If a palindrome is detected:
    • The position of the last character in the palindrome sequence (rightIdx) updates its minimum cut based on the minimum cuts before the palindrome sequence started minCuts[leftIdx - 1].
    • cutPosition is calculated conditionally depending if the palindrome starts from the beginning of the string or not.
    • Update the minCuts array with the minimum number of cuts calculated.
  • Return the last value in the minCuts array, which represents the minimum cuts required for the entire string.

This approach ensures that every substring check contributes to the overall minimum count by directly updating the global minCuts array as optimal substrings are found. The two-pointer technique used in expanding from potential palindrome centers is crucial for keeping the algorithm efficient and effective.

python
class Solution:
    def computeMinCuts(self, string):
        minCuts = [0] * len(string)
        for index in range(1, len(string)):
            minCuts[index] = index
        for midpoint in range(len(string)):
            self.processCuts(midpoint, midpoint, minCuts, string)
            self.processCuts(midpoint - 1, midpoint, minCuts, string)
        return minCuts[-1]
    
    def processCuts(self, startIdx, endIdx, minCuts, string):
        left = startIdx
        right = endIdx
        while left >= 0 and right < len(string) and string[left] == string[right]:
            optimalCut = 0 if left == 0 else minCuts[left - 1] + 1
            minCuts[right] = min(minCuts[right], optimalCut)
            left -= 1
            right += 1

The provided Python code defines a solution to determine the minimum number of cuts needed to partition a given string into palindrome substrings. The code involves creating an array called minCuts to store the minimum numbers of cuts required to make all partitions up to each position in the string palindrome. This array is initially set such that the number of cuts at each position is maximized (i.e., each character is its own segment).

The core logic of the solution involves two primary methods within the Solution class:

  • computeMinCuts: This method initializes the minCuts array and iteratively examines possible palindromes centered at each position in the string using two points of expansion (one for even lengths and another for odd lengths). For each center, it calls the processCuts method to update the minCuts array based on the calculations from the current center.

  • processCuts: This method receives start and end indexes (for potential palindromes), which it adjusts inwardly or outwardly within a loop constrained by palindrome conditions. If a palindrome condition holds, it updates the minCuts array optimally based on previously computed values and current palindrome bounds.

The code functions by minimizing the entries in the minCuts array using dynamic programming principles. For each possible center of a palindrome, both odd and even palindrome structures are considered by expanding around the center. When a palindrome substring is found that can potentially reduce the number of cuts required up to the end of the substring, minCuts is updated with the minimum value between its current count of cuts and the count given by treating the substring as a single unit (plus the optimal cuts computed for the substring’s left-side).

The method returns minCuts[-1], which provides the minimum number of cuts required for the whole string after loops have been executed to process each potential palindrome center.

Comments

No comments yet.