Dynamic Programming | Set 17 (Palindrome Partitioning)
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.
Solution
This problem is a variation of Matrix Chain Multiplication problem. If the string is 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.
// Dynamic Programming Solution for Palindrome Partitioning Problem
#include <stdio.h>
#include <string.h>
#include <limits.h>
// 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 http://www.geeksforgeeks.org/archives/15553 )*/
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_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[0][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;
}
Output:
Min cuts needed for Palindrome Partitioning is 3
Time Complexity: O(n^3)
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Intelligent
can be done in O(n^2) using dp. check leetcode for details
so basically, you don't need variable starting point. just start from 0 instead of n possible points. cut n^3 to n^2
#include"stdio.h" #include "malloc.h" int min(int a, int b,int c) { if(a<b) { return a<c?a:c; } else return b<c?b:c; } int isPalindrome(char str[],int start,int end) { while(start<end) { if(str[start] != str[end]) return 0; start++; end--; } return 1; } void findMinCuts(char str[],int len) { int *temp=(int *)malloc(sizeof(int)*(len+1)); int i,j,k,flag=0; for(i=0;i<len;i++) temp[i]=9999; temp[len]=-1; for(i=len-1;i>=0;i--) { flag=0; for(j=len-1;j>i;j--) { if(str[i] == str[j]) { if(isPalindrome(str,i,j)) { temp[i]=min(temp[i],temp[j+1]+1,temp[i+1]+1); flag=1;; } } } if(flag==0) { temp[i]=temp[i+1]+1; } } printf("\nMinimum number of cuts are: %d\n",temp[0]); printf("\n Array is: \n"); for(i=0;i<=len;i++) printf(" %c:%d ",str[i],temp[i]); printf("\n"); } void main() { char string[]="abbaacdcaabb"; findMinCuts(string,12); }I thought of an alternate algo using Divide and Conquer..
Break string into 2 parts and merge them into 1 if after merging they are palindromes....
int merge(int lb1,int ub1,int lb2,int ub2) : merges only if after merging they are palindromes..if merging done=1 else =0..
void part(int lb,int ub) //send intially lb=0 ub=n-1...
{
static int merge_count=0;
mid=(lb+ub)/2;
part(lb,mid);
part(mid+1,ub);
merge_count=merge_count+merge(lb,mid,mid+1,ub);
}
merge_count counts number of mergings done..
Min number of cuts = merge_count (Think it carefully!)....
Sorry some changes...
1. Insert a special character '*' in between each character
initially to signify cuts...
At merging,remove this cut...else this cut(*) remains..
2. At the end,count the number of '*' cuts ==> x
Then, min number of cuts = x;
This implementation uses just one array for storing
int minCuts(char *str) { int n=strlen(str),i,j,l; int t[n][n]; memset(t,-1,n*n*sizeof(int)); for(i=0;i<n-1;++i) { t[i][i]=0; if(str[i]==str[i+1]) t[i][i+1]=0; else t[i][i+1]=1; } t[i][i]=0; for(l=3;l<=n;++l) { for(i=0;i<=n-l;++i) { int k=i+l-1; if(str[i]==str[k] && t[i+1][k-1]==0) t[i][k]=0; else { t[i][k]=INT_MAX; for(j=i;j<k;++j) { int m=t[i][j]+t[j+1][k]+1; if(m< t[i][k]) t[i][k]=m; } } } } return t[0][n-1]; }void pc(char a[],int n) { int m[n+1]; for(int i=0;i<=n;i++) m[i]=i; m[0]=0; for(int i=0;i<=n;i++) { for(int j=i-1;j>=0;j--) { if(palindrom(a,j,i)) {if(j-1>=0) m[i]=m[j-1]+1; else m[i]=m[j];} } m[i]=(m[i]<m[i-1]+1)?(m[i]):(m[i-1]+1); } cout<<m[n]; }This is a normal recursive program which works perfectly, why we need to solve it like Matrix Chain Multiplication problem ?
private static int minPalPartition(char[] s, int i, int j) { if (i == j) return 0; if (isPalindrome(s, i, j)) return 0; int min = Integer.MAX_VALUE; for (int x = i; x < j; x++) { min = Math.min(min, minPalPartition(s, i, x) + 1 + minPalPartition(s, x + 1, j)); } return min; }You are not storing solutions to sub-problems anywhere, if you draw that recursion tree you will find lots of repeated computations.
import java.util.HashMap; public class MinimumDivisionsForPalindrome { public static void main(String str[]){ String st0 = "cba"; String st1 = "ababbbabbababa"; String st2 = "ababbcgdgcbbaba"; String st3 = "cgdgcababbbabb"; //int p = partition(st1); System.out.println(FindMinimumSubPalindromes(0, st0.length()-1, st0.toCharArray())); System.out.println(FindMinimumSubPalindromes(0, st1.length()-1, st1.toCharArray())); System.out.println(FindMinimumSubPalindromes(0, st2.length()-1, st2.toCharArray())); System.out.println(FindMinimumSubPalindromes(0, st3.length()-1, st3.toCharArray())); } private static int FindMinimumSubPalindromes(int start,int end,char[] toChatv) { if(AlreadyCalculated.Has(start,end,toChatv))return AlreadyCalculated.get(start,end,toChatv); if(checkforPalindrome(start,end,toChatv)) return 0; int min =999; for(int i=start;i<end;i++){ int val1 = FindMinimumSubPalindromes(i+1,end,toChatv) ; int val2 = FindMinimumSubPalindromes(start,i,toChatv); AlreadyCalculated.put(i+1,end,val1); AlreadyCalculated.put(start,i,val2); if(min > val1+val2+1) min=val1+val2+1; } return min; } private static boolean checkforPalindrome(int i, int i2, char[] toChatv) { if(i==i2)return true; int k=i; int j=i2; for(;k<j;k++,j--){ if(toChatv[k]!=toChatv[j]) return false; } return true; } } class AlreadyCalculated{ public static HashMap h = new HashMap(); public static boolean Has(int start,int end,char[] val){ return h.containsKey(""+start+""+end); } public static int get(int start,int end,char[] val){ return (Integer) h.get(""+start+""+end); } public static void put(int start,int end,int value){ h.put(""+start+""+end, value); } }Following is Naive recursive solution for the same problem
#include <stdio.h> #include <string.h> #include <limits.h> // A utility function to get minimum of two integers int min (int a, int b) { return (a < b)? a : b; } // A utility function to check whether str[i..j] is // palindrome or not bool isPal (char *str, int i, int j) { if (i == j) return true; if (str[i] == str[j] && isPal(str, i+1, j-1)) return true; return false; } // Returns the minimum number of cuts needed to partition a string // such that every part is a palindrome int minPalPartion(char *str, int i, int j) { // Base Case: If string length is 1 if (i == j) return 0; // if string is palindrome, then no cuts needed if (isPal(str, i, j) == true) return 0; // Try making first cut at all differnt positions and // recursively calculate the cuts needed for the two parts int min_cuts = INT_MAX; for (int k=i; k<=j-1; k++) min_cuts = min (min_cuts, minPalPartion(str, i, k) + minPalPartion(str, k+1, j) + 1); return min_cuts; } // Driver program to test above function int main() { char str[] = "aaaa"; int n = strlen(str); printf("Min cuts needed for Palindromic Partitioning is %d", minPalPartion(str, 0, n-1)); return 0; }#include<iostream>
using namespace std;
bool palindrom(char a[],int j,int i);
void pc(char a[],int n);
int main()
{
char a[100];
cin>>a;
pc(a,strlen(a)-1);
system("pause");
}
void pc(char a[],int n)
{
int m[n+1];
for(int i=0;i<=n;i++)
m[i]=i;
m[0]=0;
for(int i=0;i<=n;i++)
{
for(int j=i-1;j>=0;j--)
{
if(palindrom(a,j,i))
{if(j-1>=0) m[i]=m[j-1]+1; else m[i]=m[j];}
}
m[i]=(m[i]<m[i-1]+1)?(m[i]):(m[i-1]+1);
}
cout<<m[n];
}
bool palindrom(char a[],int j,int i)
{
int m=j;
int n=i;
while( m<=n)
{
if(a[m]!=a[n])
return false;
m++;
n--;
}
return true;
}
this is n^2 soln..
for any prob please comment
@rock hammer.......your soln takes O(n^3) as well since u introduce a nested loop in pc(..) and inside dat u call palindrome(..) which takes O(n),so the overall complexity stands out to b O(n^3)..correct me if I m wrong
m(i): min no of cuts for string a(0,i)
recursive eqn for dp::
m(j)= min(
m(j-i-1)+1: if a(i,j) is palindrom
where 0<=i<j ,
m(j-1)+1
)
@GeeksforGeeks : Can u please remove keyword 'dynamic programming'
from question...because I want to guess/try my best before you see ur solution...thank u guys..
I am sorry to wrong keyword above.... before I see your solution