Palindrome Partitioning | DP-17

Given a string, a partitioning of the string is a palindrome partitioning if every substring of the partition is a palindrome. For example, “aba|b|bbabb|a|b|aba” is a palindrome partitioning of “ababbbabbababa”. Determine the fewest cuts needed for palindrome partitioning of a given string. For example, minimum 3 cuts are needed for “ababbbabbababa”. The three cuts are “a|babbbab|b|ababa”. If a string is palindrome, then minimum 0 cuts are needed. If a string of length n containing all different characters, then minimum n-1 cuts are needed. Recommended: Please solve it on “PRACTICE” first, before moving on to the solution.

This problem is a variation of Matrix Chain Multiplication problem. If the string is a palindrome, then we simply return 0. Else, like the Matrix Chain Multiplication problem, we try making cuts at all possible places, recursively calculate the cost for each cut and return the minimum value.

Let the given string be str and minPalPartion() be the function that returns the fewest cuts needed for palindrome partitioning. following is the optimal substructure property.

// i is the starting index and j is the ending index. i must be passed as 0 and j as n-1
minPalPartion(str, i, j) = 0 if i == j. // When string is of length 1.
minPalPartion(str, i, j) = 0 if str[i..j] is palindrome.

// If none of the above conditions is true, then minPalPartion(str, i, j) can be
// calculated recursively using the following formula.
minPalPartion(str, i, j) = Min { minPalPartion(str, i, k) + 1 +
minPalPartion(str, k+1, j) }
where k varies from i to j-1

Following is Dynamic Programming solution. It stores the solutions to subproblems in two arrays P[][] and C[][], and reuses the calculated values.

C++

 // Dynamic Programming Solution for // Palindrome Partitioning Problem  #include using namespace std;    // A utility function to get minimum of two integers  int min(int a, int b) { return (a < b) ? a : b; }     // Returns the minimum number of cuts needed to partition a string  // such that every part is a palindrome  int minPalPartion(char* str)  {      // Get the length of the string      int n = strlen(str);         /* Create two arrays to build the solution in bottom up manner      C[i][j] = Minimum number of cuts needed for palindrome partitioning                  of substring str[i..j]      P[i][j] = true if substring str[i..j] is palindrome, else false      Note that C[i][j] is 0 if P[i][j] is true */     int C[n][n];      bool P[n][n];         int i, j, k, L; // different looping variables         // Every substring of length 1 is a palindrome      for (i = 0; i < n; i++)      {          P[i][i] = true;          C[i][i] = 0;      }         /* L is substring length. Build the     solution in bottom up manner by      considering all substrings of      length starting from 2 to n.      The loop structure is same as Matrx     Chain Multiplication problem      ( See https:// www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ )*/     for (L = 2; L <= n; L++)      {          // For substring of length L, set          // different possible starting indexes          for (i = 0; i < n - L + 1; i++)         {              j = i + L - 1; // Set ending index                 // If L is 2, then we just need to              // compare two characters. Else              // need to check two corner characters             // and value of P[i+1][j-1]              if (L == 2)                  P[i][j] = (str[i] == str[j]);              else                 P[i][j] = (str[i] == str[j]) && P[i + 1][j - 1];                 // IF str[i..j] is palindrome, then C[i][j] is 0              if (P[i][j] == true)                  C[i][j] = 0;              else              {                  // Make a cut at every possible                  // location starting from i to j,                  // and get the minimum cost cut.                  C[i][j] = INT_MAX;                  for (k = i; k <= j - 1; k++)                      C[i][j] = min(C[i][j], C[i][k] + C[k + 1][j] + 1);              }          }      }         // Return the min cut value for     // complete string. i.e., str[0..n-1]      return C[n - 1];  }     // Driver code  int main()  {      char str[] = "ababbbabbababa";      cout<<"Min cuts needed for Palindrome Partitioning is "<

C

 // Dynamic Programming Solution for Palindrome Partitioning Problem #include #include #include    // A utility function to get minimum of two integers int min(int a, int b) { return (a < b) ? a : b; }    // Returns the minimum number of cuts needed to partition a string // such that every part is a palindrome int minPalPartion(char* str) {     // Get the length of the string     int n = strlen(str);        /* Create two arrays to build the solution in bottom up manner        C[i][j] = Minimum number of cuts needed for palindrome partitioning                  of substring str[i..j]        P[i][j] = true if substring str[i..j] is palindrome, else false        Note that C[i][j] is 0 if P[i][j] is true */     int C[n][n];     bool P[n][n];        int i, j, k, L; // different looping variables        // Every substring of length 1 is a palindrome     for (i = 0; i < n; i++) {         P[i][i] = true;         C[i][i] = 0;     }        /* L is substring length. Build the solution in bottom up manner by        considering all substrings of length starting from 2 to n.        The loop structure is same as Matrx Chain Multiplication problem (        See https:// www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ )*/     for (L = 2; L <= n; L++) {         // For substring of length L, set different possible starting indexes         for (i = 0; i < n - L + 1; i++) {             j = i + L - 1; // Set ending index                // If L is 2, then we just need to compare two characters. Else             // need to check two corner characters and value of P[i+1][j-1]             if (L == 2)                 P[i][j] = (str[i] == str[j]);             else                 P[i][j] = (str[i] == str[j]) && P[i + 1][j - 1];                // IF str[i..j] is palindrome, then C[i][j] is 0             if (P[i][j] == true)                 C[i][j] = 0;             else {                 // Make a cut at every possible location starting from i to j,                 // and get the minimum cost cut.                 C[i][j] = INT_MAX;                 for (k = i; k <= j - 1; k++)                     C[i][j] = min(C[i][j], C[i][k] + C[k + 1][j] + 1);             }         }     }        // Return the min cut value for complete string. i.e., str[0..n-1]     return C[n - 1]; }    // Driver program to test above function int main() {     char str[] = "ababbbabbababa";     printf("Min cuts needed for Palindrome Partitioning is %d",            minPalPartion(str));     return 0; }

Java

 // Java Code for Palindrome Partitioning // Problem public class GFG {     // Returns the minimum number of cuts needed     // to partition a string such that every     // part is a palindrome     static int minPalPartion(String str)     {         // Get the length of the string         int n = str.length();            /* Create two arrays to build the solution            in bottom up manner            C[i][j] = Minimum number of cuts needed                       for palindrome partitioning                      of substring str[i..j]            P[i][j] = true if substring str[i..j] is                      palindrome, else false            Note that C[i][j] is 0 if P[i][j] is            true */         int[][] C = new int[n][n];         boolean[][] P = new boolean[n][n];            int i, j, k, L; // different looping variables            // Every substring of length 1 is a palindrome         for (i = 0; i < n; i++) {             P[i][i] = true;             C[i][i] = 0;         }            /* L is substring length. Build the solution in          bottom up manner by considering all substrings          of length starting from 2 to n. The loop           structure is same as Matrx Chain Multiplication          problem (         See https:// www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ )*/         for (L = 2; L <= n; L++) {             // For substring of length L, set different             // possible starting indexes             for (i = 0; i < n - L + 1; i++) {                 j = i + L - 1; // Set ending index                    // If L is 2, then we just need to                 // compare two characters. Else need to                 // check two corner characters and value                 // of P[i+1][j-1]                 if (L == 2)                     P[i][j] = (str.charAt(i) == str.charAt(j));                 else                     P[i][j] = (str.charAt(i) == str.charAt(j)) && P[i + 1][j - 1];                    // IF str[i..j] is palindrome, then                 // C[i][j] is 0                 if (P[i][j] == true)                     C[i][j] = 0;                 else {                     // Make a cut at every possible                     // localtion starting from i to j,                     // and get the minimum cost cut.                     C[i][j] = Integer.MAX_VALUE;                     for (k = i; k <= j - 1; k++)                         C[i][j] = Integer.min(C[i][j],                                               C[i][k] + C[k + 1][j] + 1);                 }             }         }            // Return the min cut value for complete         // string. i.e., str[0..n-1]         return C[n - 1];     }        // Driver program to test above function     public static void main(String args[])     {         String str = "ababbbabbababa";         System.out.println("Min cuts needed for "                            + "Palindrome Partitioning is " + minPalPartion(str));     } } // This code is contributed by Sumit Ghosh

Python3

 # Dynamic Programming Solution for  # Palindrome Partitioning Problem     # Returns the minimum number of  # cuts needed to partition a string # such that every part is a palindrome def minPalPartion(str):            # Get the length of the string     n = len(str)            # Create two arrays to build the      # solution in bottom up manner      # C[i][j] = Minimum number of cuts      # needed for palindrome      # partitioning of substring str[i..j]      # P[i][j] = true if substring str[i..j]      # is palindrome, else false. Note that     # C[i][j] is 0 if P[i][j] is true      C = [[0 for i in range(n)]              for i in range(n)]     P = [[False for i in range(n)]                  for i in range(n)]        # different looping variables     j = 0     k = 0     L = 0            # Every substring of length      # 1 is a palindrome      for i in range(n):         P[i][i] = True;          C[i][i] = 0;                 # L is substring length. Build the      # solution in bottom up manner by      # considering all substrings of      # length starting from 2 to n.      # The loop structure is same as      # Matrix Chain Multiplication problem       # (See https://www.geeksforgeeks.org / matrix-chain-multiplication-dp-8/ )     for L in range(2, n + 1):                    # For substring of length L, set          # different possible starting indexes          for i in range(n - L + 1):             j = i + L - 1 # Set ending index                             # If L is 2, then we just need to             # compare two characters. Else              # need to check two corner characters             # and value of P[i + 1][j-1]             if L == 2:                  P[i][j] = (str[i] == str[j])             else:                 P[i][j] = ((str[i] == str[j]) and                               P[i + 1][j - 1])                                             # IF str[i..j] is palindrome,              # then C[i][j] is 0             if P[i][j] == True:                 C[i][j] = 0             else:                                    # Make a cut at every possible                  # location starting from i to j,                 # and get the minimum cost cut.                 C[i][j] = 100000000                 for k in range(i, j):                     C[i][j] = min (C[i][j], C[i][k] +                                     C[k + 1][j] + 1)                                           # Return the min cut value for      # complete string. i.e., str[0..n-1]      return C[n - 1]    # Driver code str = "ababbbabbababa" print ('Min cuts needed for Palindrome Partitioning is',                                       minPalPartion(str))                                          # This code is contributed  # by sahil shelangia

C#

 // C# Code for Palindrome Partitioning // Problem using System;    class GFG {     // Returns the minimum number of cuts needed     // to partition a string such that every     // part is a palindrome     static int minPalPartion(String str)     {         // Get the length of the string         int n = str.Length;            /* Create two arrays to build the solution         in bottom up manner         C[i][j] = Minimum number of cuts needed                      for palindrome partitioning                     of substring str[i..j]         P[i][j] = true if substring str[i..j] is                     palindrome, else false         Note that C[i][j] is 0 if P[i][j] is         true */         int[, ] C = new int[n, n];         bool[, ] P = new bool[n, n];            int i, j, k, L; // different looping variables            // Every substring of length 1 is a palindrome         for (i = 0; i < n; i++) {             P[i, i] = true;             C[i, i] = 0;         }            /* L is substring length. Build the solution in         bottom up manner by considering all substrings         of length starting from 2 to n. The loop          structure is same as Matrx Chain Multiplication         problem (         See https:// www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/ )*/         for (L = 2; L <= n; L++) {             // For substring of length L, set different             // possible starting indexes             for (i = 0; i < n - L + 1; i++) {                 j = i + L - 1; // Set ending index                    // If L is 2, then we just need to                 // compare two characters. Else need to                 // check two corner characters and value                 // of P[i+1][j-1]                 if (L == 2)                     P[i, j] = (str[i] == str[j]);                 else                     P[i, j] = (str[i] == str[j]) && P[i + 1, j - 1];                    // IF str[i..j] is palindrome, then                 // C[i][j] is 0                 if (P[i, j] == true)                     C[i, j] = 0;                 else {                     // Make a cut at every possible                     // localtion starting from i to j,                     // and get the minimum cost cut.                     C[i, j] = int.MaxValue;                     for (k = i; k <= j - 1; k++)                         C[i, j] = Math.Min(C[i, j], C[i, k]                                                         + C[k + 1, j] + 1);                 }             }         }            // Return the min cut value for complete         // string. i.e., str[0..n-1]         return C[0, n - 1];     }        // Driver program     public static void Main()     {         String str = "ababbbabbababa";         Console.Write("Min cuts needed for "                       + "Palindrome Partitioning is " + minPalPartion(str));     } }    // This code is contributed by Sam007

PHP



Output:

Min cuts needed for Palindrome Partitioning is 3

Time Complexity: O(n3)

An optimization to above approach
In the above approach, we can calculate the minimum cut while finding all palindromic substring. If we find all palindromic substring 1st and then we calculate minimum cut, time complexity will reduce to O(n2).
Thanks for Vivek for suggesting this optimization.

C++

 // Dynamic Programming Solution for Palindrome Partitioning Problem #include #include #include    // A utility function to get minimum of two integers int min(int a, int b) { return (a < b) ? a : b; }    // Returns the minimum number of cuts needed to partition a string // such that every part is a palindrome int minPalPartion(char* str) {     // Get the length of the string     int n = strlen(str);        /* Create two arrays to build the solution in bottom-up manner        C[i] = Minimum number of cuts needed for a palindrome partitioning                  of substring str[0..i]        P[i][j] = true if substring str[i..j] is palindrome, else false        Note that C[i] is 0 if P[i] is true */     int C[n];     bool P[n][n];        int i, j, k, L; // different looping variables        // Every substring of length 1 is a palindrome     for (i = 0; i < n; i++) {         P[i][i] = true;     }        /* L is substring length. Build the solution in bottom up manner by        considering all substrings of length starting from 2 to n. */     for (L = 2; L <= n; L++) {         // For substring of length L, set different possible starting indexes         for (i = 0; i < n - L + 1; i++) {             j = i + L - 1; // Set ending index                // If L is 2, then we just need to compare two characters. Else             // need to check two corner characters and value of P[i+1][j-1]             if (L == 2)                 P[i][j] = (str[i] == str[j]);             else                 P[i][j] = (str[i] == str[j]) && P[i + 1][j - 1];         }     }        for (i = 0; i < n; i++) {         if (P[i] == true)             C[i] = 0;         else {             C[i] = INT_MAX;             for (j = 0; j < i; j++) {                 if (P[j + 1][i] == true && 1 + C[j] < C[i])                     C[i] = 1 + C[j];             }         }     }        // Return the min cut value for complete string. i.e., str[0..n-1]     return C[n - 1]; }    // Driver program to test above function int main() {     char str[] = "ababbbabbababa";     printf("Min cuts needed for Palindrome Partitioning is %d",            minPalPartion(str));     return 0; }

Java

 // Java Code for Palindrome Partitioning // Problem public class GFG {     // Returns the minimum number of cuts needed     // to partition a string such that every part     // is a palindrome     static int minPalPartion(String str)     {         // Get the length of the string         int n = str.length();            /* Create two arrays to build the solution         in bottom up manner            C[i] = Minimum number of cuts needed for            palindrome partitioning of substring            str[0..i]            P[i][j] = true if substring str[i..j] is             palindrome, else false            Note that C[i] is 0 if P[i] is true */         int[] C = new int[n];         boolean[][] P = new boolean[n][n];            int i, j, k, L; // different looping variables            // Every substring of length 1 is a palindrome         for (i = 0; i < n; i++) {             P[i][i] = true;         }            /* L is substring length. Build the solution          in bottom up manner by considering all substrings          of length starting from 2 to n. */         for (L = 2; L <= n; L++) {             // For substring of length L, set different             // possible starting indexes             for (i = 0; i < n - L + 1; i++) {                 j = i + L - 1; // Set ending index                    // If L is 2, then we just need to                 // compare two characters. Else need to                 // check two corner characters and value                 // of P[i+1][j-1]                 if (L == 2)                     P[i][j] = (str.charAt(i) == str.charAt(j));                 else                     P[i][j] = (str.charAt(i) == str.charAt(j)) && P[i + 1][j - 1];             }         }            for (i = 0; i < n; i++) {             if (P[i] == true)                 C[i] = 0;             else {                 C[i] = Integer.MAX_VALUE;                 for (j = 0; j < i; j++) {                     if (P[j + 1][i] == true && 1 + C[j] < C[i])                         C[i] = 1 + C[j];                 }             }         }            // Return the min cut value for complete         // string. i.e., str[0..n-1]         return C[n - 1];     }        // Driver program to test above function     public static void main(String args[])     {         String str = "ababbbabbababa";         System.out.println("Min cuts needed for "                            + "Palindrome Partitioning"                            + " is " + minPalPartion(str));     } } // This code is contributed by Sumit Ghosh

Python3

 # Dynamic Programming Solution for  # Palindrome Partitioning Problem  import sys    # Returns the minimum number of cuts  # needed to partition a string such  # that every part is a palindrome  def minPalPartion(str1):            # Get the length of the string     n = len(str1);            # Create two arrays to build the solution     # in bottom up manner     # C[i] = Minimum number of cuts needed     # for palindrome partitioning of     # substring str[0..i]     # P[i][j] = true if substring str[i..j]     # is palindrome, else false     # Note that C[i] is 0 if P[i] is true     C = *(n+1);     P = [[False for x in range(n+1)] for y in range(n+1)];            # Every substring of length 1 is     # a palindrome     for i in range(n):         P[i][i] = True;            # L is substring length. Build the solution     # in bottom up manner by considering all     # substrings of length starting from 2 to n.     for L in range(2, n + 1):                    # For substring of length L, set         # different possible starting indexes         for i in range(n - L + 1):             j = i + L - 1;                            # Set ending index             # If L is 2, then we just need to             # compare two characters. Else need             # to check two corner characters and             # value of P[i+1][j-1]             if (L == 2):                 P[i][j] = (str1[i] == str1[j]);             else:                 P[i][j] = ((str1[i] == str1[j]) and P[i + 1][j - 1]);     for i in range(n):         if (P[i] == True):             C[i] = 0;         else:             C[i] = sys.maxsize;             for j in range(i):                 if(P[j + 1][i] == True and 1 + C[j] < C[i]):                     C[i] = 1 + C[j];            # Return the min cut value for complete     # string. i.e., str[0..n-1]     return C[n - 1];     # Driver Code str1 = "ababbbabbababa";  print("Min cuts needed for Palindrome Partitioning is",minPalPartion(str1));     # This code is contributed by mits

C#

 // C# Code for Palindrome Partitioning // Problem using System;    class GFG {        // Returns the minimum number of cuts needed     // to partition a string such that every part     // is a palindrome     static int minPalPartion(String str)     {         // Get the length of the string         int n = str.Length;            /* Create two arrays to build the solution         in bottom up manner         C[i] = Minimum number of cuts needed for         palindrome partitioning of substring         str[0..i]         P[i][j] = true if substring str[i..j] is          palindrome, else false         Note that C[i] is 0 if P[i] is true */         int[] C = new int[n];         bool[, ] P = new bool[n, n];            int i, j, L; // different looping variables            // Every substring of length 1 is a palindrome         for (i = 0; i < n; i++) {             P[i, i] = true;         }            /* L is substring length. Build the solution          in bottom up manner by considering all substrings          of length starting from 2 to n. */         for (L = 2; L <= n; L++) {             // For substring of length L, set different             // possible starting indexes             for (i = 0; i < n - L + 1; i++) {                 j = i + L - 1; // Set ending index                    // If L is 2, then we just need to                 // compare two characters. Else need to                 // check two corner characters and value                 // of P[i+1][j-1]                 if (L == 2)                     P[i, j] = (str[i] == str[j]);                 else                     P[i, j] = (str[i] == str[j]) && P[i + 1, j - 1];             }         }            for (i = 0; i < n; i++) {             if (P[0, i] == true)                 C[i] = 0;             else {                 C[i] = int.MaxValue;                 for (j = 0; j < i; j++) {                     if (P[j + 1, i] == true && 1 + C[j] < C[i])                         C[i] = 1 + C[j];                 }             }         }            // Return the min cut value for complete         // string. i.e., str[0..n-1]         return C[n - 1];     }        // Driver program     public static void Main()     {         String str = "ababbbabbababa";         Console.Write("Min cuts needed for "                       + "Palindrome Partitioning"                       + " is " + minPalPartion(str));     } }    // This code is contributed by Sam007

PHP



Output:

Min cuts needed for Palindrome Partitioning is 3

Time Complexity: O(n2)

Using Memorization to solve this problem.
The basic idea is to cache the intermittent results calculated in recursive functions. We can put these results into a hashmap/unordered_map.
To calculate the keys for the Hashmap we will use the starting and end index of the string as the key i.e. [“start_index”.append(“end_index”)] would be the key for the Hashmap.
Below is the implementation of above approach :

 // Using memoizatoin to solve the partition problem. #include using namespace std; // Function to check if input string is pallindrome or not bool ispallindrome(string input, int start, int end) {     // Using two pointer technique to check pallindrome     while (start < end) {         if (input[start] != input[end])             return false;         start++;         end--;     }     return true; }    // Function to find keys for the Hashmap string convert(int a, int b) {     return to_string(a) + "" + to_string(b); }    // Returns the minimum number of cuts needed to partition a string // such that every part is a palindrome int minpalparti_memo(string input, int i, int j, unordered_map& memo) {     if (i > j)         return 0;     // Key for the Input String     string ij = convert(i, j);        // If the no of partitions for string "ij" is already calculated     // then return the calculated value using the Hashmap     if (memo.find(ij) != memo.end()) {         return memo[ij];     }     // Every String of length 1 is a pallindrome     if (i == j) {         memo[ij] = 0;         return 0;     }     if (ispallindrome(input, i, j)) {         memo[ij] = 0;         return 0;     }     int minimum = INT_MAX;     // Make a cut at every possible location starting from i to j     for (int k = i; k < j; k++) {         int left_min = INT_MAX;         int right_min = INT_MAX;         string left = convert(i, k);         string right = convert(k + 1, j);            // If left cut is found already         if (memo.find(left) != memo.end()) {             left_min = memo[left];         }         // If right cut is found already         if (memo.find(right) != memo.end()) {             right_min = memo[right];         }            // Recursively calculating for left and right strings         if (left_min == INT_MAX)             left_min = minpalparti_memo(input, i, k, memo);         if (right_min == INT_MAX)             right_min = minpalparti_memo(input, k + 1, j, memo);            // Taking minimum of all k possible cuts         minimum = min(minimum, left_min + 1 + right_min);     }        memo[ij] = minimum;     // Return the min cut value for complete string.     return memo[ij]; } int main() {     string input = "ababbbabbababa";     unordered_map memo;     cout << minpalparti_memo(input, 0, input.length() - 1, memo) << endl;     return 0; }

Time Complexity: O(n2)

My Personal Notes arrow_drop_up