import java.util.*;
public class Solution {
/**
* 解法一
* 时间复杂度: O(N^3)
* 空间复杂度: O(N)
* 以下以 [x,y] 表示 s 的第 x 个字符到第 y 个字符的字串
* 这里主要考虑的是动态规划
* 状态定义: dp[i] = [1,i]的最小切割数
* 状态转移: 设 任意 a <= i
* 当[a,i]是回文串: dp[i] = min(dp[i],dp[a-1]+1)
* 当[a,i]不是回文串: dp[i] 不做改变
* 初始化: dp[i] = i-1 (即最坏就是将每个字符都切割开)
* 这里将 dp[0]设置为 -1 很关键,因为如果第 [1,i]为回文串,则
* dp[a-1]+1 即 dp[0]+1 应为 0,即dp[0]需要设置为 -1
* 返回值: dp[s.length]
* @param s
* @return
*/
public int minCut (String s) {
//检验合法性
if (s == null || s.length() == 0) {
return -1;
}
//状态数组
int[] dp = new int[s.length() + 1];
//初始化
for (int i = 0; i <= s.length(); i++) {
dp[i] = i - 1;
}
//状态转移
for (int i = 2; i <= s.length(); i++) {
for (int a = 1; a <= i; a++) {
if (isPal(s, a, i)) {
dp[i] = Math.min(dp[i], dp[a - 1] + 1);
}
}
}
return dp[s.length()];
}
/**
* 判断 s 的从第left个字符到第right字符的字串是否为回文串(两边均为闭区间)
* @param s
* @param left
* @param right
* @return
*/
private static boolean isPal(String s, int left, int right) {
while (left < right) {
if (s.charAt(left - 1) != s.charAt(right - 1)) {
return false;
}
left++;
right--;
}
return true;
}
/**
* 解法二 (针对解法一的时间复杂度进行优化)
* 时间复杂度 O(N^2)
* 空间复杂度 O(N^2)
* 解法一的时间复杂度为 O(N^3),实在太高了,因此我们必须进行优化
* 对于解法一的状态定义和转移方程我们很难进行优化,因此这里我们对求是否是回文串进行优化
* 即 先求出字符串 s 中所有的是回文串的字串,之后在进行状态转移的时候不用判定每个子串是否为回文串
* 同时通过动态规划求出字符串 s 中所有的是回文串的字串的时间复杂度为 O(N^2),
* 因此整体时间复杂度就从 O(N^3) 降到了 O(N^2)
* 具体做法如下:
* 以下以 [x,y] 表示 s 的第 x 个字符到第 y 个字符的字串
* 状态定义: dp[i][j] = true 当且仅当 [i,j]是回文串 (i <= j)
* 状态转移: 当 i+1 == j时: dp[i][j] = true 当且仅当 s 的第 i 个字符和第 j 个字符相等
* 其余情况时: dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i+1][j-1]
* 初始化: 当 i == j时: dp[i][j] = true
* 返回值: 返回这个 dp数组 即可
*
* 特别注意这个动态规划的状态转移过程,因为 i <= j 是前提条件,因此我们只需要遍历矩阵的右上三角即可,
* 同时从转移方程中我们可以得出 dp[i][j] 和 dp[i+1][j-1]的值有关,即和左下角的值有关
* 因此我们在进行状态转移的时候需要从下网上遍历
*
* @param s
* @return
*/
public int minCut2 (String s) {
//检验合法性
if (s == null || s.length() == 0) {
return -1;
}
//得到标记回文串的二维数组
boolean[][] mat = getMat(s);
//状态数组
int[] dp = new int[s.length() + 1];
//初始化
for (int i = 0; i <= s.length(); i++) {
dp[i] = i - 1;
}
//状态转移
for (int i = 2; i <= s.length(); i++) {
for (int a = 1; a <= i; a++) {
if (mat[a][i]) {
dp[i] = Math.min(dp[i], dp[a - 1] + 1);
}
}
}
return dp[s.length()];
}
private static boolean[][] getMat (String s) {
//状态定义
boolean[][] dp = new boolean[s.length() + 1][s.length() + 1];
//状态初始化
for (int i = 0; i <= s.length(); i++) {
dp[i][i] = true;
}
//状态转移
for (int i = s.length() - 1; i >= 1; i--) {
for (int j = s.length(); j > i; j--) {
if (i + 1 == j) {
dp[i][j] = (s.charAt(i - 1) == s.charAt(j - 1));
} else {
dp[i][j] = (s.charAt(i - 1) == s.charAt(j - 1)) && dp[i + 1][j - 1];
}
}
}
return dp;
}
}