一. 回文子串
回文子串
- 状态表示
 将所有子串是否是回文串的信息, 保存在dp表中
 dp[i][j] 表示 从i到j的子串是否是回文串
- 状态转移方程
 如果s[i] != s[j] 一定不是回文子串
 如果s[i] == s[j] 分三种情况:
 1.i == j 指同一个字符, 一定是回文串 true
 2.i + 1 == j 两个字符, 一定是回文串 true
 3.dp[i][j] = dp[i + 1][j - 1]
- 初始化
 无需初始化 上面的条件已经帮我们完成
- 填表顺序
 从下往上, 从左往右
- 返回值
 返回true的个数
class Solution {public int countSubstrings(String s) {int n = s.length();char[] ss = s.toCharArray();boolean[][] dp = new boolean[n][n];int ret = 0;for (int i = n - 1; i >= 0; i--) {for (int j = i; j < n; j++) {if (ss[i] == ss[j]) {if (i == j) {dp[i][j] = true;} else if (i + 1 == j) {dp[i][j] = true;} else {dp[i][j] = dp[i + 1][j - 1];}}if (dp[i][j]) {ret++;}}}return ret;}
}
二. 最长回文子串
最长回文子串
class Solution {public String longestPalindrome(String s) {int n = s.length();boolean[][] dp = new boolean[n][n];int p = 0, len = 0;for(int i = n; i >= 0; i--){for(int j = i; j < n; j++){if(s.charAt(i) == s.charAt(j)){if(i == j || i + 1 == j) dp[i][j] = true;else dp[i][j] = dp[i + 1][j - 1]; }if(dp[i][j] && j - i + 1 > len){p = i; len = j - i + 1;}}}return s.substring(p, p + len);}
}
三. 回文串分割IV
回文串分割
 用i j 表示中间子串的开始和结束位置
 字符串被分成dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1] 三部分
class Solution {public boolean checkPartitioning(String s) {int n = s.length();boolean[][] dp = new boolean[n][n];for(int i = n - 1; i >= 0; i--){for(int j = i; j < n; j++){if(s.charAt(i) == s.charAt(j)){if(i == j || i + 1 == j) dp[i][j] = true;else dp[i][j] = dp[i + 1][j - 1];}}}for(int i = 1; i < n - 1; i++){for(int j = i; j < n - 1; j++){if(dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1]) return true;}}return false;}
}
四. 分割回文串II
- 状态表示
 dp[i][j] 表示 (0, i)字符串中, 需要分割的最小次数
- 状态转移方程
 1.(0, i)是回文串, dp[i] = 0;
 2.(0, i)不是回文串, 分成两种情况
 定义j, 0 < j <= i
 如果(j, i)是回文串, dp[i] = dp[i - 1] + 1
 如果(j, i)不是回文串, 判断下一个位置
- dp[i] = min(dp[i - 1] + 1)
- 初始化
 dp每个元素初始化为最大值, 防止干扰结果
- 填表顺序
 从左往右
- 返回值
 dp[n - 1]
class Solution {public int minCut(String s) {int n = s.length();boolean[][] tmp = new boolean[n][n];int[] dp = new int[n];Arrays.fill(dp, Integer.MAX_VALUE);for (int i = n - 1; i >= 0; i--) {for (int j = i; j < n; j++) {if (s.charAt(i) == s.charAt(j)) {if (i == j || i + 1 == j)tmp[i][j] = true;elsetmp[i][j] = tmp[i + 1][j - 1];}}}for (int i = 0; i < n; i++) {if (tmp[0][i])dp[i] = 0;else {for (int j = 1; j <= i; j++) {if (tmp[j][i])dp[i] = Math.min(dp[j - 1] + 1, dp[i]);}}}return dp[n - 1];}
}五. 最长回文子序列
最长回文子序列
-  状态表示 
 dp[i][j] 表示 (i, j)字符串中, 最长回文子序列
-  状态转移方程 
 如果s[i] == s[j] :
 1.i == j , 最长为1
 2.i +1 = j, 最长为2
 3.其余情况, 要找i + 1 和 j - 1 的最长回文子序列 + 2
 如果s[i] != s[j] : 说明不是ij都选, 只选择其中一个, 求最大值
 dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
-  初始化 
 无需初始化, 不会越界
-  填表顺序 
 从左往右, 从下往上
-  返回值 
 dp[0][n - 1]
class Solution {public int longestPalindromeSubseq(String s) {int n = s.length();int[][] dp = new int[n][n];for (int i = n - 1; i >= 0; i--) {dp[i][i] = 1;for (int j = i + 1; j < n; j++) {if (s.charAt(i) == s.charAt(j)) {dp[i][j] = dp[i + 1][j - 1] + 2;} else {dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);}}}return dp[0][n - 1];}
}
六. 让字符串成为回文串的最少插入次数
让字符串成为回文串的最少插入次数
- 状态表示
 dp[i][j] 表示 (i, j)字符串中, 让字符串成为回文串的最少插入次数
- 状态转移方程
 如果s[i] == s[j] :
 1.i == j , 最少次数为0
 2.i +1 = j, 最少次数为0
 3.其余情况, 要找i + 1 和 j - 1 的最少次数, 为dp[i + 1][j - 1]
 如果s[i] != s[j]
 可以在 i 之前插入s[j], dp[i][j] = dp[i][j - 1] + 1
 可以在 j 之后插入s[i], dp[i][j] = dp[i + 1][j] + 1
- dp[i][j] = min(dp[i][j - 1] + 1, dp[i + 1][j] + 1)
- 初始化
 无需初始化, 不会越界
- 填表顺序
 从左往右, 从下往上
- 返回值
 dp[0][n - 1]
class Solution {public int minInsertions(String s) {int n = s.length();int[][] dp = new int[n][n];for (int i = n - 1; i >= 0; i--) {for (int j = i + 1; j < n; j++) {if (s.charAt(i) == s.charAt(j)) {if (i + 1 < j) {dp[i][j] = dp[i + 1][j - 1];}} else {dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1;}}}return dp[0][n - 1];}
}
