The first part of the algorithm is to create a table to tell us if a given string and the substrings within it are parlindrome.
We mark a substring as palindrome in the table if
a) Characters at the beginning and end of the substring match and
b) Characters in between make a palindrome.
A two dimensional table is constructed to mark substring from position i till j as a palindrome. For palindrome_table [ i ] [ j ] to be true, in between substring i.e substring from ( i + 1 ) and ( j - 1 ) should also be a palindrome.
This dynamic programming approach is bottom-up in nature i.e we find out the palindromes from length 1 all the way till the length of the given string.
Example:
Base Case(s):
Time complexity to check if the substring is a palindrome : O ( N 2 ), where N is the length of the string.
Method 1 has a time complexity of O ( n 3 ). It makes use of the table constructed earlier to check if a substring is a palindrome.
Consider the substring [ K A Y Y A ] from the original string [ K A Y Y A K ]. The minimum number of cuts needed to make [ K A Y Y A ] a palindrome is 1
i.e [ K ] & [ A Y Y A ].
So the idea is to put the cut in all the possible places. This can be done for all possible substring in a bottom up fashion.
Given String | Substring | Cut Position | Minimum Cuts |
---|---|---|---|
[ K A Y Y A K ] | [ K A Y Y A ] is not a palindrome. |
[ K ] - [ A Y Y A ] [ K A ] - [ Y Y A ] [ K A Y ] - [ Y A ] [ K A Y Y ] - [ A ] |
mincuts ( K ) + mincuts ( A Y Y A ) + 1 = 0 + 0 + 1 = 1 mincuts ( K A ) + mincuts ( Y Y A ) + 1 = 1 + 1 + 1 = 3 mincuts ( K A Y ) + mincuts ( Y A ) + 1 = 2 + 1 + 1 = 4 mincuts ( K A Y Y ) + mincuts ( A ) + 1 = 2 + 0 + 1 = 3 |
Program : O ( n 3 ) solution for finding the minimum cuts needed to make a string palindrome (s)
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
int MinimumCuts (string str) {
const int N = str.size();
// String of size 0 or 1 will not need any cuts to make it a palindrome
if (N == 1 or N == 0) {
return 0;
}
bool palindrome_table[N][N];
memset(palindrome_table, false, sizeof(bool) * N * N);
// Every single character is a palindrome.
for (int i = 0; i < N; i++) {
palindrome_table[i][i] = true;
}
// Check if 2 adjacent characters make a palindrome.
for (int i = 0; i < N - 1; i++) {
if (str.at(i) == str.at(i+1)) {
palindrome_table[i][i+1] = true;
}
}
// Create a table for checking if a substring [i, j] is a palindrome.
for (int len = 3; len <= N; len++) {
for (int beg = 0; beg < N - len + 1; beg++) {
int end = beg + len - 1;
if (str.at(beg) == str.at(end) and palindrome_table[beg+1][end-1] == true) {
palindrome_table[beg][end] = true;
}
}
}
//Create a table to keep track of minimum cuts needed to make the string palindrome.
int mincuts[N][N];
memset (mincuts, 0, sizeof(int) * N * N);
for(int i = 0; i < N - 1; i++) {
if (str.at(i) == str.at(i+1)) {
mincuts[i][i+1] = 0; // If adjacent characters are same, minimum cuts needed are 0.
} else {
mincuts[i][i+1] = 1; // // If adjacent characters are different, minimum cuts needed are 1
}
}
// For string of length >= 3 build the mincuts table in bottom up fashion.
for (int len = 3; len <= N; len++) {
for (int beg = 0; beg < N - len + 1; beg++) {
int end = beg + len - 1;
if(palindrome_table[beg][end] == false) { // Substring [i,j] is not a palindrome.
mincuts[beg][end] = N; // set minimum cuts to a max number
for (int cut = beg; cut < end; cut++) {
// Consider the string abcba, cuts would be ( a - bcba | ab - cba | abc - ba | abcb - a )
mincuts[beg][end] = min (mincuts[beg][end], mincuts[beg][cut] + mincuts[cut+1][end] + 1);
}
}
}
}
return mincuts[0][N-1];
};
int main() {
vector<string> strings = { "aaa", "abcbc", "kayyak", "efe", "alfaaflay", "alfaaflayalfaaflay", "abc","algootre" };
for (const auto& str : strings) {
cout << "Minimum cuts needed to make string [ " << str << " ] a palindrome : " << MinimumCuts (str) << endl;
}
return 0;
}
Output
Minimum cuts needed to make string [ aaa ] a palindrome : 0
Minimum cuts needed to make string [ abcbc ] a palindrome : 2
Minimum cuts needed to make string [ kayyak ] a palindrome : 0
Minimum cuts needed to make string [ efe ] a palindrome : 0
Minimum cuts needed to make string [ alfaaflay ] a palindrome : 1
Minimum cuts needed to make string [ alfaaflayalfaaflay ] a palindrome : 1
Minimum cuts needed to make string [ abc ] a palindrome : 2
Minimum cuts needed to make string [ algootre ] a palindrome : 6
Method II takes O ( n 2 ) time to contruct the table for finding the minimum cuts. Consider the below example.
The suffix appends to all the possible prefixes to form the longest palindrome.
Given String | Substring | Prefix | Suffix | Minimum cuts needed to make a palindrome |
---|---|---|---|---|
[ K A Y Y A K ] | K | None | K | 0 ( single character make a palindrome ) |
[ K A Y Y A K ] | K A | K | A | 1 ( adjacent characters are different so 1 cut is needed for substring [ K A ] ) |
[ K A Y Y A K ] | K A Y | K A | Y | A Y is not a palindrome, minimum cuts needed are 1 + minimum_cuts [ K A ] = 1 + 1 = 2 |
[ K A Y Y A K ] | K A Y Y | K A Y | Y | Y Y is a palindrome, minimum cuts needed are 1 + minimum_cuts [ K A ] = 1 + 1 = 2 |
[ K A Y Y A K ] | K A Y Y A | K A Y Y | A | A Y Y A is a palindrome, minimum cuts needed are 1 + minimum_cuts [ K ] = 1 + 0 = 1 |
[ K A Y Y A K ] | K A Y Y A K | K A Y Y A | K | K A Y Y A K is a palindrome, minimum cuts needed = 0 |
Thus we get the tables as below
Substring | K | K A | K A Y | K A Y Y | K A Y Y A | K A Y Y A K |
Index | 0 | 1 | 2 | 3 | 4 | 5 |
Minimum Cuts | 0 Base case |
1 Base case |
2 | 2 | 1 | 0 |
Program : O ( n 2 ) solution for finding the minimum cuts needed to make a string palindrome (s)
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
int MinimumCuts (string str) {
const int N = str.size();
// String of size 0 or 1 will not need any cuts to make it a palindrome
if (N == 1 or N == 0) {
return 0;
}
bool palindrome_table[N][N];
memset(palindrome_table, false, sizeof(bool) * N * N);
// Every single character is a palindrome.
for(int i=0; i<N; i++) {
palindrome_table[i][i] = true;
}
// Check if 2 adjacent characters make a palindrome.
for(int i=0; i<N-1; i++) {
if (str.at(i) == str.at(i+1)) {
palindrome_table[i][i+1] = true;
}
}
// Create a table for checking if a substring [i, j] is a palindrome.
for(int len=3; len <= N; len++) {
for(int i=0; i < N-len+1; i++) {
int j = i + len - 1;
if (str.at(i) == str.at(j) and palindrome_table[i+1][j-1] == true) {
palindrome_table[i][j] = true;
}
}
}
//Create a minimum cuts suffix table to keep track of the cuts needed to make a string palindrome.
int mincuts[N];
memset (mincuts, 0, sizeof(int) * N);
mincuts[0] = 0;
if (str.at(0) == str.at(1)) {
mincuts[1] = 0;
} else {
mincuts[1] = 1;
}
for (int current = 2; current < N; current++) {
// set minimum cuts to previously found cuts + 1 (presume that it needs 1 more cut).
mincuts[current] = mincuts[current-1] + 1;
for (int prev = current-1; prev >= 0; prev --) {
// Check if the substring [prev, current] is a palindrome
if (palindrome_table[prev][current] == true) {
if (prev > 0) {
mincuts[current] = min (mincuts[current], 1 + mincuts[prev-1]);
} else if (prev == 0) {
mincuts[current] = 0; // the substring [0, current] is a palindrome.
}
}
}
}
return mincuts[N-1];
};
int main() {
vector<string> strings = { "aaa", "abcbc", "kayyak", "efe", "alfaaflay", "alfaaflayalfaaflay", "abc","algootre", "fiefhgdcdcgfeibggchibffahiededbbegegdfibdbfdadfbdbceaadeceeefiheibahgececggaehbdcgebaigfacifhdbecbebfhiefchaaheiichgdbheacfbhfiaffaecicbegdgeiaiccghggdfggbebdaefcagihbdhhigdgbghbahhhdagbdaefeccfiaifffcfehfcdiiieibadcedibbedgfegibefagfccahfcbegdfdhhdgfhgbchiaieehdgdabhidhfeecgfiibediiafacagigbhchcdhbaigdcedggehhgdhedaebchcafcdehcffdiagcafcgiidhdhedgaaegdchibhdaegdfdaiiidcihifbfidechicighbcbgibadbabieaafgeagfhebfaheaeeibagdfhadifafghbfihehgcgggffgbfccgafigieadfehieafaehaggeeaaaehggffccddchibegfhdfafhadgeieggiigacbfgcagigbhbhefcadafhafdiegahbhccidbeeagcgebehheebfaechceefdiafgeddhdfcadfdafbhiifigcbddahbabbeedidhaieagheihhgffbfbiacgdaifbedaegbhigghfeiahcdieghhdabdggfcgbafgibiifdeefcbegcfcdihaeacihgdchihdadifeifdgecbchgdgdcifedacfddhhbcagaicbebbiadgbddcbagbafeadhddaeebdgdebafabghcabdhdgieiahggddigefddccfccibifgbfcdccghgceigdfdbghdihechfabhbacifgbiiiihcgifhdbhfcaiefhccibebcahidachfabicbdabibiachahggffiibbgchbidfbbhfcicfafgcagaaadbacddfiigdiiffhbbehaaacidggfbhgeaghigihggfcdcidbfccahhgaffiibbhidhdacacdfebedbiacaidaachegffaiiegeabfdgdcgdacfcfhdcbfiaaifgfaciacfghagceaaebhhibbieehhcbiggabefbeigcbhbcidbfhfcgdddgdffghidbbbfbdhcgabaagddcebaechbbiegeiggbabdhgghciheabdibefdfghbfbfebidhicdhbeghebeddgfdfhefebiiebdchifbcbahaddhbfafbbcebiigadhgcfbebgbebhfddgdeehhgdegaeedfadegfeihcgeefbbagbbacbgggciehdhiggcgaaicceeaefgcehfhfdciaghcbbgdihbhecfbgffefhgiefgeiggcebgaacefidghdfdhiabgibchdicdehahbibeddegfciaeaffgbefbbeihbafbagagedgbdadfdggfeaebaidchgdbcifhahgfdcehbahhdggcdggceiabhhafghegfdiegbcadgaecdcdddfhicabdfhbdiiceiegiedecdifhbhhfhgdbhibbdgafhgdcheefdhifgddchadbdggiidhbhegbdfdidhhfbehibiaacdfbiagcbheabaaebfeaeafbgigiefeaeheabifgcfibiddadicheahgbfhbhddaheghddceedigddhchecaghdegigbegcbfgbggdgbbigegffhcfcbbebdchffhddbfhhfgegggibhafiebcfgeaeehgdgbccbfghagfdbdfcbcigbigaccecfehcffahiafgabfcaefbghccieehhhiighcfeabffggfchfdgcfhadgidabdceediefdccceidcfbfiiaidechhbhdccccaigeegcaicabbifigcghcefaafaefd" };
for (const auto& str : strings) {
cout << "Minimum cuts needed to make string [ " << str << " ] a palindrome : " << MinimumCuts (str) << endl;
}
return 0;
}
Output
Minimum cuts needed to make string [ aaa ] a palindrome : 0
Minimum cuts needed to make string [ abcbc ] a palindrome : 2
Minimum cuts needed to make string [ kayyak ] a palindrome : 0
Minimum cuts needed to make string [ efe ] a palindrome : 0
Minimum cuts needed to make string [ alfaaflay ] a palindrome : 1
Minimum cuts needed to make string [ alfaaflayalfaaflay ] a palindrome : 1
Minimum cuts needed to make string [ abc ] a palindrome : 2
Minimum cuts needed to make string [ algootre ] a palindrome : 6
Minimum cuts needed to make string [ fiefhgdcdcgfeibggchibffahiededbbegegdfibdbfdadfbdbceaadeceeefiheibahgececggaehbdcgebaigfacifhdbecbebfhiefchaaheiichgdbheacfbhfiaffaecicbegdgeiaiccghggdfggbebdaefcagihbdhhigdgbghbahhhdagbdaefeccfiaifffcfehfcdiiieibadcedibbedgfegibefagfccahfcbegdfdhhdgfhgbchiaieehdgdabhidhfeecgfiibediiafacagigbhchcdhbaigdcedggehhgdhedaebchcafcdehcffdiagcafcgiidhdhedgaaegdchibhdaegdfdaiiidcihifbfidechicighbcbgibadbabieaafgeagfhebfaheaeeibagdfhadifafghbfihehgcgggffgbfccgafigieadfehieafaehaggeeaaaehggffccddchibegfhdfafhadgeieggiigacbfgcagigbhbhefcadafhafdiegahbhccidbeeagcgebehheebfaechceefdiafgeddhdfcadfdafbhiifigcbddahbabbeedidhaieagheihhgffbfbiacgdaifbedaegbhigghfeiahcdieghhdabdggfcgbafgibiifdeefcbegcfcdihaeacihgdchihdadifeifdgecbchgdgdcifedacfddhhbcagaicbebbiadgbddcbagbafeadhddaeebdgdebafabghcabdhdgieiahggddigefddccfccibifgbfcdccghgceigdfdbghdihechfabhbacifgbiiiihcgifhdbhfcaiefhccibebcahidachfabicbdabibiachahggffiibbgchbidfbbhfcicfafgcagaaadbacddfiigdiiffhbbehaaacidggfbhgeaghigihggfcdcidbfccahhgaffiibbhidhdacacdfebedbiacaidaachegffaiiegeabfdgdcgdacfcfhdcbfiaaifgfaciacfghagceaaebhhibbieehhcbiggabefbeigcbhbcidbfhfcgdddgdffghidbbbfbdhcgabaagddcebaechbbiegeiggbabdhgghciheabdibefdfghbfbfebidhicdhbeghebeddgfdfhefebiiebdchifbcbahaddhbfafbbcebiigadhgcfbebgbebhfddgdeehhgdegaeedfadegfeihcgeefbbagbbacbgggciehdhiggcgaaicceeaefgcehfhfdciaghcbbgdihbhecfbgffefhgiefgeiggcebgaacefidghdfdhiabgibchdicdehahbibeddegfciaeaffgbefbbeihbafbagagedgbdadfdggfeaebaidchgdbcifhahgfdcehbahhdggcdggceiabhhafghegfdiegbcadgaecdcdddfhicabdfhbdiiceiegiedecdifhbhhfhgdbhibbdgafhgdcheefdhifgddchadbdggiidhbhegbdfdidhhfbehibiaacdfbiagcbheabaaebfeaeafbgigiefeaeheabifgcfibiddadicheahgbfhbhddaheghddceedigddhchecaghdegigbegcbfgbggdgbbigegffhcfcbbebdchffhddbfhhfgegggibhafiebcfgeaeehgdgbccbfghagfdbdfcbcigbigaccecfehcffahiafgabfcaefbghccieehhhiighcfeabffggfchfdgcfhadgidabdceediefdccceidcfbfiiaidechhbhdccccaigeegcaicabbifigcghcefaafaefd ] a palindrome : 1345