import java.util.*;
/**
* Given a string s, partition s such that every substring of the partition is
* a palindrome.
*
* Return the minimum cuts needed for a palindrome partitioning of s.
*
* For example, given s = "aab",
* Return 1 since the palindrome partitioning ["aa","b"] could be produced
* using 1 cut.
*
* Tags: DP
*/
class PalindromePartition {
public static void main(String[] args) {
/*test palindrome*/
// System.out.println(isPalindrome("a"));
// System.out.println(isPalindrome("aa"));
// System.out.println(isPalindrome("aaa"));
// System.out.println(isPalindrome("aab"));
// System.out.println(isPalindrome("aabb"));
// System.out.println(isPalindrome("abba"));
/*test minCut*/
System.out.println(minCut("a"));
System.out.println(minCut("aa"));
System.out.println(minCut("aab"));
System.out.println(minCut("aabbcc"));
System.out.println(minCut("aabbccdd"));
System.out.println(minCut("abcdcba"));
System.out.println(minCut("abcd"));
}
/**
* Each cut at i+j is calculated by scanning (i-j)'s minimum cut + 1 if
* s[i-j, i+j] is a palindrome.
*/
public int minCut(String s) {
if(s == null || s.length() == 0) return 0;
int len = s.length();
int[] cuts = new int[len + 1]; // store results
for (int i = 0; i <= len; i++) cuts[i] = i - 1; // max cuts
for (int i = 0; i < len; i++) {
// odd palin
for (int j = 0; i - j >= 0 && i + j < len && s.charAt(i - j) == s.charAt(i + j); j++) cuts[i + j + 1] = Math.min(cuts[i + j + 1], 1 + cuts[i - j]);
// even palin
for (int j = 1; i - j + 1 >= 0 && i + j < len && s.charAt(i - j + 1) == s.charAt(i + j); j++) cuts[i + j + 1] = Math.min(cuts[i + j + 1], 1 + cuts[i - j + 1]);
}
return cuts[len];
}
/**
* Calculate and maintain 2 DP states:
*
* pal[i][j] , which is whether s[i..j] forms a pal
*
* d[i], which is the minCut for s[i..n-1]
*
* Once we comes to a pal[i][j]==true:
*
* if j==n-1, the string s[i..n-1] is a Pal, minCut is 0, d[i]=0;
* else: the current cut num (first cut s[i..j] and then cut the rest
* s[j+1...n-1]) is 1+d[j+1], compare it to the exisiting minCut num d[i],
* repalce if smaller.
*/
public int minCut(String s) {
if (s == null || s.length() == 0) return 0;
int len = s.length();
boolean[][] p = new boolean[len][len];
for (int i = 0; i < len; i++) Arrays.fill(p[i], false);
int[] results = new int[len];
for (int start = len - 1; start >= 0; start--) {
results[start] = len - start - 1;
for (int end = start; end < len; end++) {
if (s.charAt(start) == s.charAt(end)) {
if (end - start < 2) p[start][end] = true;
else
p[start][end] = p[start + 1][end - 1];
}
if (p[start][end]) {
if (end = len - 1) results[start] = 0;
else results[start] = Math.min(results[start], results[end + 1] + 1);
}
}
}
return results[0];
}
/**
* Backtracking, generate all cuts
*/
public static int minCut(String s) {
Set<String> palin = new HashSet<String>();
return minCut(s, 0, palin);
}
public static int minCut(String s, int count, Set<String> palin) {
// System.out.println("s: " + s + " \tcount: " + count);
if (s == null || s.length() == 0 || isPalindrome(s)) {
palin.add(s);
return count;
}
int min = Integer.MAX_VALUE;
for (int i = s.length() - 1; i >= 0; i--) {
if (isPalindrome(s.substring(0, i))) {
palin.add(s.substring(0, i));
// add DP here
int result = palin.contains(s.substring(i)) ? count : minCut(s.substring(i), count + 1, palin);;
min = Math.min(min, result);
}
}
return min;
}
/**
* judge whether a string is a Palindrome
*/
private static boolean isPalindrome(String s) {
if (s == null || s.length() == 0) return false;
if (s.length() == 1) return true;
int i = 0;
int len = s.length();
while (i < len / 2) {
if (s.charAt(i) != s.charAt(len - i - 1)) return false;
i++;
}
return true;
}
}