Skip to content

Commit 8077ad1

Browse files
Merge pull request #562 from bhumikasahai/palindrome-dp-7thPR
Add DP solution for Palindrome Partitioning problem in Java
2 parents de6abca + 1520e99 commit 8077ad1

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
class PalindromePartitioning {
2+
3+
/**
4+
* Calculates the minimum number of cuts needed to partition a string such that every
5+
* substring of the partition is a palindrome.
6+
*
7+
* ALGORITHM DESCRIPTION:
8+
* --------------------
9+
* This problem asks for the minimum cuts, which suggests an optimization problem that is
10+
* well-suited for Dynamic Programming. The solution involves two main DP components.
11+
*
12+
* STAGE 1: PRE-COMPUTING ALL PALINDROMIC SUBSTRINGS
13+
* Before we can find the minimum cuts, we need an efficient way to check if any given
14+
* substring is a palindrome. We can pre-compute this information.
15+
*
16+
* State Definition:
17+
* We use a 2D boolean table `isPalindrome[i][j]`, where `isPalindrome[i][j]` is `true`
18+
* if the substring from index `i` to `j` (inclusive) is a palindrome, and `false` otherwise.
19+
*
20+
* Logic:
21+
* - A single character is always a palindrome (`isPalindrome[i][i] = true`).
22+
* - A substring of length > 1 from `i` to `j` is a palindrome if:
23+
* 1. The characters at the ends match (`s.charAt(i) == s.charAt(j)`).
24+
* 2. The inner substring from `i+1` to `j-1` is also a palindrome.
25+
* We can fill this table in O(N^2) time.
26+
*
27+
* STAGE 2: FINDING THE MINIMUM CUTS
28+
* With our palindrome information ready, we can now find the minimum cuts.
29+
*
30+
* State Definition:
31+
* We use a 1D integer array `cuts[i]`, where `cuts[i]` stores the minimum number of
32+
* cuts needed for the prefix of the string of length `i+1` (i.e., `s.substring(0, i+1)`).
33+
*
34+
* Logic:
35+
* - Initialize `cuts[i] = i`, which represents the worst-case scenario where we cut
36+
* after every character (e.g., "abc" -> "a|b|c" needs 2 cuts for a prefix of length 3).
37+
* - We then iterate `i` from 0 to the end of the string. For each `i`, we check all
38+
* substrings ending at `i`. Let's say a substring starts at `j` (where `0 <= j <= i`).
39+
* - If `s.substring(j, i+1)` is a palindrome (which we can check in O(1) from our table):
40+
* - It means the entire segment from `j` to `i` needs 0 cuts.
41+
* - The total cuts needed for the prefix up to `i` would be `1 +` (the minimum cuts
42+
* needed for the prefix ending at `j-1`).
43+
* - We update `cuts[i]` with the minimum value found among all such valid `j`.
44+
* - A special case: if the entire prefix `s.substring(0, i+1)` is a palindrome (`j=0`),
45+
* then `cuts[i]` is 0.
46+
*
47+
* Final Answer: The result is `cuts[s.length() - 1]`.
48+
*
49+
* COMPLEXITY ANALYSIS:
50+
* --------------------
51+
* Let N be the length of the string `s`.
52+
*
53+
* Time Complexity: O(N^2)
54+
* - Stage 1 (building the `isPalindrome` table) takes O(N^2).
55+
* - Stage 2 (building the `cuts` array) has two nested loops, also taking O(N^2).
56+
* - Total time complexity is O(N^2) + O(N^2) = O(N^2).
57+
*
58+
* Space Complexity: O(N^2)
59+
* - The `isPalindrome` table requires O(N^2) space.
60+
* - The `cuts` array requires O(N) space.
61+
* - The dominant factor is the 2D table, so the total space complexity is O(N^2).
62+
*
63+
* @param s The input string.
64+
* @return The minimum number of cuts required.
65+
*/
66+
public int minCuts(String s) {
67+
// Step 1: Handle edge cases.
68+
if (s == null || s.length() <= 1) {
69+
return 0;
70+
}
71+
int n = s.length();
72+
73+
// --- STAGE 1: Pre-compute the palindrome table ---
74+
// isPalindrome[i][j] is true if substring s[i..j] is a palindrome.
75+
boolean[][] isPalindrome = new boolean[n][n];
76+
77+
for (int len = 1; len <= n; len++) {
78+
for (int i = 0; i <= n - len; i++) {
79+
int j = i + len - 1;
80+
// Check if characters at ends match and if the inner substring is also a palindrome.
81+
if (s.charAt(i) == s.charAt(j) && (len <= 2 || isPalindrome[i + 1][j - 1])) {
82+
isPalindrome[i][j] = true;
83+
}
84+
}
85+
}
86+
87+
// --- STAGE 2: Calculate the minimum cuts using the palindrome table ---
88+
// cuts[i] = minimum cuts for the prefix s[0..i].
89+
int[] cuts = new int[n];
90+
91+
for (int i = 0; i < n; i++) {
92+
// Check if the whole prefix s[0..i] is a palindrome.
93+
if (isPalindrome[0][i]) {
94+
cuts[i] = 0; // 0 cuts needed.
95+
} else {
96+
// Worst-case: cut after every character.
97+
cuts[i] = i;
98+
// Find the minimum cuts by checking all possible last palindromic substrings.
99+
// If s[j..i] is a palindrome, then we can make a cut after s[j-1].
100+
for (int j = 1; j <= i; j++) {
101+
if (isPalindrome[j][i]) {
102+
// The number of cuts would be 1 (for the new cut) + cuts[j-1] (for the prefix).
103+
cuts[i] = Math.min(cuts[i], 1 + cuts[j - 1]);
104+
}
105+
}
106+
}
107+
}
108+
109+
// The final answer is the minimum cuts for the entire string s[0..n-1].
110+
return cuts[n - 1];
111+
}
112+
113+
public static void main(String[] args) {
114+
PalindromePartitioning pp = new PalindromePartitioning();
115+
116+
// Example 1: "aab" -> "aa" | "b" -> 1 cut
117+
String s1 = "aab";
118+
System.out.println("The minimum cuts for \"" + s1 + "\" is: " + pp.minCuts(s1)); // Expected: 1
119+
120+
// Example 2: "racecar" is already a palindrome
121+
String s2 = "racecar";
122+
System.out.println("The minimum cuts for \"" + s2 + "\" is: " + pp.minCuts(s2)); // Expected: 0
123+
124+
// Example 3: A more complex case
125+
String s3 = "ababbbabbababa";
126+
System.out.println("The minimum cuts for \"" + s3 + "\" is: " + pp.minCuts(s3)); // Expected: 3
127+
128+
// Example 4: All different characters
129+
String s4 = "abcde";
130+
System.out.println("The minimum cuts for \"" + s4 + "\" is: " + pp.minCuts(s4)); // Expected: 4
131+
}
132+
}

0 commit comments

Comments
 (0)