Palindrome Partitioning

Updated on 02 July, 2025
Palindrome Partitioning header image

Problem Statement

In this task, you need to partition a given string s such that every substring in the partitioned list is a palindrome. A palindrome is a sequence of characters which reads the same backward as forwards. Your goal is to generate all potential ways to split the string s into palindrome segments and return these segmentations.

Examples

Example 1

Input:

s = "aab"

Output:

[["a","a","b"],["aa","b"]]

Example 2

Input:

s = "a"

Output:

[["a"]]

Constraints

  • 1 <= s.length <= 16
  • s contains only lowercase English letters.

Approach and Intuition

When partitioning the string s into substrings that are all palindromes, the primary challenge lies in systematically exploring different partition configurations and verifying if they lead to all palindromic subsequences. Here’s a breakdown of our approach:

  1. Use a depth-first search (DFS) algorithm to try every possible way of splitting the string while making recursive calls to partition the remainder of the string.

  2. At each step, begin by identifying if a substring (starting from the current index to any possible end index) is a palindrome:

    • If it is a palindrome, make a recursive DFS call with the remainder of the string past this end index.
    • Append the palindrome to a temporary list that holds the current partition state.
  3. If you reach the end of the string during your DFS recursion, it means the current partitioning is complete, and all segments are palindromic. In such cases, add the temporary list to your result list.

  4. Make sure to backtrack by removing the last added palindrome from your list once you have explored all possibilities with that prefix, ensuring all potential partitions are explored.

  • This approach ensures that for each valid palindromic segment you detect in the string, you recursively explore further splitting the rest of the string.
  • Utilizing a helper function can significantly ease the process of checking if a substring is a palindrome. This function could simply compare the substring to its reverse and return the result.

This method efficiently explores each possible partitioning, ensuring complete coverage due to its recursive nature. The constraints provided (string length between 1 and 16) allow this depth-first search approach to operate within acceptable performance limits.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
class Partitioner {
public:
    vector<vector<string>> partitionStrings(string s) {
        int length = s.length();
        vector<vector<bool>> palindromeDp(length, vector<bool>(length, false));
        vector<vector<string>> output;
        vector<string> path;
        recursivePartition(output, s, 0, path, palindromeDp);
        return output;
    }
    
    void recursivePartition(vector<vector<string>> &output, string &s, int startIndex,
                             vector<string> &path, vector<vector<bool>> &palindromeDp) {
        if (startIndex >= s.length()) output.push_back(path);
        for (int end = startIndex; end < s.length(); end++) {
            if (s[startIndex] == s[end] &&
                (end - startIndex <= 2 || palindromeDp[startIndex + 1][end - 1])) {
                palindromeDp[startIndex][end] = true;
                path.push_back(s.substr(startIndex, end - startIndex + 1));
                recursivePartition(output, s, end + 1, path, palindromeDp);
                path.pop_back();
            }
        }
    }
};

The given C++ code defines a solution for the problem of partitioning a string into all possible palindromic substrings. Here's how the solution is structured:

  • a class Partitioner contains all functionalities.
  • the method partitionStrings used to initiate the partitioning process.
  • recursivePartition, a private helper method, explores all partitioning possibilities using backtracking.

The solution employs a dynamic programming table, palindromeDp, to memorize intermediate results, helping to optimize the palindrome checks. This table records if substrings of s are palindromes:

  • The outer loop in recursivePartition attempts to partition the string starting from each index up to the end.
  • For any substring that is identified as a palindrome, the method adds it to the current partition (path) and moves on to the next possible partition point recursively.
  • If startIndex surpasses or reaches the length of the string, the function records the current partition as one of the results.

The process works efficiently by:

  • Ensuring substrings are only considered palindromes based on previously confirmed palindromes, thus making the palindrome checking constant time inside the loop.
  • Backtracking allows the exploration of all possible partitions and retracts them if they don't lead to a solution.

This approach ensures that all palindromic partitions of the string are captured, making it effective for solving problems involving string partitioning based on palindromic properties.

java
class Solution {
    public List<List<String>> findAllPalindromes(String text) {
        int textLength = text.length();
        boolean[][] isPalindrome = new boolean[textLength][textLength];
        List<List<String>> output = new ArrayList<>();
        recursePalindrome(output, text, 0, new ArrayList<>(), isPalindrome);
        return output;
    }
    
    void recursePalindrome(
        List<List<String>> output,
        String text,
        int index,
        List<String> current,
        boolean[][] isPalindrome
    ) {
        if (index >= text.length()) output.add(new ArrayList<>(current));
        for (int finish = index; finish < text.length(); finish++) {
            if (
                text.charAt(index) == text.charAt(finish) &&
                (finish - index <= 2 || isPalindrome[index + 1][finish - 1])
            ) {
                isPalindrome[index][finish] = true;
                current.add(text.substring(index, finish + 1));
                recursePalindrome(output, text, finish + 1, current, isPalindrome);
                current.remove(current.size() - 1);
            }
        }
    }
}

This Java solution employs a recursive backtracking approach to solve the "Palindrome Partitioning" problem, where the objective is to find all possible ways to split a given text such that every substring is a palindrome.

The solution defines a class Solution with the method findAllPalindromes:

  • Initialize a 2D array isPalindrome to keep track of palindrome substrings.
  • Declare an output list output to store the list of palindrome partitions.
  • Call the recursive function recursePalindrome with necessary parameters.

The recursePalindrome method performs the core logic:

  • Base case: If index reaches the end of the string, add the current list of palindromes to output.
  • Loop from index to the end of the string, checking if the substring from index to finish is a palindrome.
  • If it is a palindrome (determined by direct character comparison or using the previously filled isPalindrome table for longer substrings), add the substring to the current list and recursively proceed to find more palindromes from the next index.
  • Backtrack by removing the last added substring when returning from recursion.

This recursive and backtracking approach efficiently explores all possible ways to partition the string into palindromes, while memoizing intermediate results in the isPalindrome array to avoid redundant computations.

c
#define LIMIT 1000
char*** segment(char* str, int* sizeOut, int** columnSizesOut) {
    int strLen = strlen(str);
    bool palinMatrix[LIMIT][LIMIT] = {false};
    char*** segmentedResult = (char***)calloc(LIMIT * LIMIT, sizeof(char**));
    char** tmpSegment = (char**)calloc(LIMIT, sizeof(char*));
    *sizeOut = 0;
    *columnSizesOut = (int*)calloc(LIMIT * LIMIT, sizeof(int));
    calculateSegments(str, 0, &segmentedResult, sizeOut, columnSizesOut, &tmpSegment, 0, palinMatrix);
    free(tmpSegment);
    return segmentedResult;
}
void calculateSegments(char* substring, int startPos, char**** resultContainer, int* sizeOut,
                       int** columnSizesOut, char*** tmpSegment, int segmentSize,
                       bool palinMatrix[][LIMIT]) {
    int sLen = strlen(substring);
    if (startPos >= sLen) {
        (*resultContainer)[*sizeOut] =
            (char**)malloc(segmentSize * sizeof(char*));
        memcpy((*resultContainer)[*sizeOut], *tmpSegment,
               segmentSize * sizeof(char*));
        (*columnSizesOut)[*sizeOut] = segmentSize;
        (*sizeOut)++;
    }
    for (int endPos = startPos; endPos < sLen; endPos++) {
        if (substring[startPos] == substring[endPos] &&
            (endPos - startPos <= 2 || palinMatrix[startPos + 1][endPos - 1])) {
            palinMatrix[startPos][endPos] = true;
            char* subpart = (char*)calloc((endPos - startPos + 2), sizeof(char));
            strncpy(subpart, &substring[startPos], endPos - startPos + 1);
            (*tmpSegment)[segmentSize] = subpart;
            calculateSegments(substring, endPos + 1, resultContainer, sizeOut, columnSizesOut, tmpSegment,
                segmentSize + 1, palinMatrix);
            (*tmpSegment)[segmentSize] = NULL;
        }
    }
}

The given C code implements a solution for the Palindrome Partitioning problem. Here's a concise outline of how the solution is structured and operates:

  • The primary function segment initializes necessary variables and memory for the solution, utilizing dynamic allocation for managing result sets and tracking positions in the input string. It processes the input string to find all possible palindrome partitions.

  • The function calculateSegments is a recursive helper function that splits the input string into all potential palindromic segments. This function uses dynamic programming techniques, leveraging a matrix (palinMatrix) to memoize results and avoid redundant calculations. The matrix keeps track of which substrings are palindromes.

  • In calculateSegments, the code iterates over possible end positions for a substring starting from a given start position (startPos). If a valid palindrome is found, it dynamically allocates space for this substring and recursively attempts to find palindromic partitions for the remainder of the string.

  • Each time a complete set of palindromic partitions is found (when startPos exceeds the string length), it copies these partitions into the result container, updates counters, and manages output sizes dynamically to handle multiple results effectively.

  • Finally, the segment function releases temporary memory used during processing and returns a triple-pointer structured dynamic array containing all the partitioned results along with their respective sizes.

By following the combination of recursive depth-first search for partitioning and dynamic programming for palindrome checking, this solution efficiently partitions the string into all combinations of palindromic subsequences, ensuring optimally using memory and computation.

js
var palindromePartitions = function (inputStr) {
    let length = inputStr.length;
    let dpTable = Array(length)
        .fill()
        .map(() => Array(length).fill(false));
    let partitions = [];
    searchPartitions(partitions, inputStr, 0, [], dpTable);
    return partitions;
};
function searchPartitions(collector, inputStr, index, path, dpTable) {
    if (index >= inputStr.length) collector.push([...path]);
    for (let i = index; i < inputStr.length; i++) {
        if (
            inputStr[index] === inputStr[i] &&
            (i - index <= 2 || dpTable[index + 1][i - 1])
        ) {
            dpTable[index][i] = true;
            path.push(inputStr.slice(index, i + 1));
            searchPartitions(collector, inputStr, i + 1, path, dpTable);
            path.pop();
        }
    }
}

The provided JavaScript function palindromePartitions aims to find all possible ways to partition a given string inputStr into substrings that are palindromes. The solution employs a recursive approach, utilizing dynamic programming to store intermediate results, thereby enhancing efficiency.

  • Begin by initializing a dpTable as a 2D array with dimensions equivalent to the length of inputStr, which is used to store whether the substrings are palindromes.
  • Declare partitions to collect the resulting list of palindrome substrings.

The core recursive function searchPartitions performs the following tasks:

  1. Check if the current index has reached the end of inputStr; if so, add the collected path of substrings (which are palindromes) to the collector.
  2. Iterate through the string from the current index to the end of inputStr, for each character checking if the substring from index to i is a palindrome by either being a single character, adjacent characters that are the same, or a longer substring that forms a palindrome according to the dpTable.
  3. If a palindrome is identified, mark it in the dpTable, add it to the current path, and recursively call searchPartitions to process the remaining string from the next character.
  4. After the recursive call, remove the last substring to explore other potential partitions.

Finally, the function returns the partitions array, each element of which is a list of substrings forming valid palindrome partitions of the input string. The use of dpTable ensures that each substring is checked for being a palindrome at most once, significantly optimizing the process and avoiding redundant checks.

python
class Solution:
    def split_palindromes(self, text: str) -> List[List[str]]:
        text_len = len(text)
        palindrome_dp = [[False] * text_len for _ in range(text_len)]
        palindromic_partitions = []
        self.find_palindromes(palindromic_partitions, text, 0, [], palindrome_dp)
        return palindromic_partitions
    
    def find_palindromes(
        self,
        partitions: List[List[str]],
        text: str,
        index: int,
        temp_partition: List[str],
        palindrome_dp: List[List[bool]],
    ):
        if index >= len(text):
            partitions.append(list(temp_partition))
        for end in range(index, len(text)):
            if text[index] == text[end] and (
                end - index <= 2 or palindrome_dp[index + 1][end - 1]
            ):
                palindrome_dp[index][end] = True
                temp_partition.append(text[index : end + 1])
                self.find_palindromes(partitions, text, end + 1, temp_partition, palindrome_dp)
                temp_partition.pop()

The provided Python code defines a solution for breaking a given string into all possible lists of substrings where each substring is a palindrome. This involves using a dynamic programming approach to check and store palindrome substrings efficiently. Below is a summary of how the code tackles this problem:

  • The solution class is structured with two primary functions, split_palindromes and find_palindromes.

  • split_palindromes initializes a 2D list palindrome_dp to track palindrome substrings within the text. This method also prepares a list palindromic_partitions to hold all palindromic partitions found and calls the recursive function find_palindromes with starting parameters.

  • find_palindromes recursively finds and stores all palindromic partitions:

    • The function iterates over substrings starting from the current index.
    • It checks conditions using an if statement to determine if the current substring from index to end is a palindrome:
      • If the characters at positions index and end are the same and either the substring is of length 3 or less or the previously computed substring is a palindrome.
    • If a palindrome is identified, it updates the tracking list palindrome_dp, appends this substring to temp_partition, and recursively checks for further palindromic partitions from end + 1.

This approach leverages both recursion for exploring each possible partition and dynamic programming for efficiently checking and storing palindromic substrings, while reducing redundant iterations and checks. This algorithm efficiently splits the input string into every combination of palindromic partitions, demonstrating a typical combination of these two programming strategies in solving complex problems.

Comments

No comments yet.