找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程程(ಥ_ಥ)-优快云博客
所属专栏:动态规划
目录
647.回文子串
题目:516.最长回文子序列
给你一个字符串
s
,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
示例 1:
输入:s = "abc" 输出:3 解释:三个回文子串: "a", "b", "c"示例 2:
输入:s = "aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"提示:
1 <= s.length <= 1000
s
由小写英文字母组成
思路:
代码实现:
class Solution {
public int countSubstrings(String s) {
// 创建dp表、初始化、填表、返回值
int n = s.length();
boolean[][] dp = new boolean[n][n];
char[] ss = s.toCharArray();
// 初始化通过for循环来完成
int ret = 0;
for (int i = n-1; i >= 0; i--) { // 从下往上
for (int j = n-1; j >= i; j--) { // 从右往左
if (ss[i] == ss[j]) {
if (i == j || i+1 == j) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i+1][j-1];
}
} // 默认为false,因此不用去赋值了
ret += dp[i][j] ? 1 : 0;
}
}
return ret;
}
}
5.最长回文子串
题目:
给你一个字符串
s
,找到s
中最长的 回文 子串。示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。示例 2:
输入:s = "cbbd" 输出:"bb"提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
思路:
本题与上一题唯一的不同,就是需要在所有的回文子串中找出最长的回文子串。处理方法,既可以是在填完dp表之后,去遍历求出最长的回文子串,也可以在填dp表时,就去统计最长回文子串的信息。
代码实现:
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
char[] ss = s.toCharArray();
// 初始化的工作通过 for循环 完成
int[] ret = new int[2]; // 记录最长回文子串的起始位置和结束位置
for (int i = n-1; i >= 0; i--) {
for (int j = n-1; j >= i; j--) {
if (ss[i] == ss[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 > ret[1] - ret[0]) {
ret[0] = i;
ret[1] = j;
}
}
}
return s.substring(ret[0], ret[1]+1); // 注意substring方法是左闭右开的
}
}
1745.分割回文串IV
题目:
给你一个字符串
s
,如果可以将它分割成三个 非空 回文子字符串,那么返回true
,否则返回false
。当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串 。
示例 1:
输入:s = "abcbdd" 输出:true 解释:"abcbdd" = "a" + "bcb" + "dd",三个子字符串都是回文的。示例 2:
输入:s = "bcbddxy" 输出:false 解释:s 没办法被分割成 3 个回文子字符串。提示:
3 <= s.length <= 2000
s
只包含小写英文字母。
思路:题目是让我们判断原字符串分割为三部分子字符串之后,判断这些子字符串是否是回文串。怎么分割成三部分呢?我们可以通过枚举的方式,枚举出分割点的位置,从而将字符串分为了[0, i-1]、[i, j]、[j+1, n-1]这三部分,接下来就是判断这三部分是否为回文子串。而通过dp表可以快速查阅子串是否为回文串。因此本题最终的处理思路是,先将dp表构建出来,然后去枚举分割点,判断分割后的字符串是否为回文串即可。
代码实现:
class Solution {
public boolean checkPartitioning(String s) {
// 1、构建完整的dp表信息
int n = s.length();
char[] ss = s.toCharArray();
boolean[][] dp = new boolean[n][n];
for (int i = n-1; i >= 0; i--) {
for (int j = n-1; j >= i; j--) {
if (ss[i] == ss[j]) {
if (i == j || i+1 == j) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i+1][j-1];
}
}
}
}
// 2、枚举所有的分割点,判断是否为回文子串
// 枚举时,得确保 [0, i-1]、[i, j]、[j+1, n-1]区间都得有值
// 从而得出边界值(i-1 >= 0,因此 i>=1, j>=i, j+1 <= n-1,因此j<=n-2)
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;
}
}
132.分割回文串II
题目:
给你一个字符串
s
,请你将s
分割成一些子串,使每个子串都是回文串。返回符合要求的 最少分割次数 。
示例 1:
输入:s = "aab" 输出:1 解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。示例 2:
输入:s = "a" 输出:0示例 3:
输入:s = "ab" 输出:1提示:
1 <= s.length <= 2000
s
仅由小写英文字母组成
思路:注意:dp表只能存储子串是否回文的信息,而不能进行其他操作。因此还得根据前面写动态规划的步骤来解决本题。
代码实现:
class Solution {
public int minCut(String s) {
// 创建dp表、初始化、填表、返回值
int n = s.length();
int[] dp = new int[n];
// 存储子串是否回文信息
boolean[][] bool = palindromeSubstring(n, s);
// // 初始化dp数组
// Arrays.fill(dp, Integer.MAX_VALUE);
// for (int i = 0; i < n; i++) {
// // 判断[0,i]是否为回文串
// if (!bool[0][i]) {
// for (int j = 1; j <= i; j++) {
// // 判断[j,i]是否为回文串
// if (bool[j][i]) {
// dp[i] = Math.min(dp[i], dp[j-1]+1);
// }
// }
// } else {
// dp[i] = 0;
// }
// }
for (int i = 0; i < n; i++) {
// 判断[0,i]是否为回文串
if (!bool[0][i]) {
int min = Integer.MAX_VALUE;
for (int j = 1; j <= i; j++) {
// 判断[j,i]是否为回文串
if (bool[j][i]) {
min = Math.min(min, dp[j-1]+1);
}
}
dp[i] = min;
}
// 默认为0,因此无需更新
}
return dp[n-1];
}
private boolean[][] palindromeSubstring(int n, String s) {
// 创建dp表、初始化、填表、返回值
boolean[][] bool = new boolean[n][n];
for (int i = n-1; i >= 0; i--) {
for (int j = n-1; j >= i; j--) {
if (s.charAt(i) == s.charAt(j)) {
if (i == j || i+1 == j) {
bool[i][j] = true;
} else {
bool[i][j] = bool[i+1][j-1];
}
}
}
}
return bool;
}
}
516.最长回文子序列
题目:
给你一个字符串
s
,找出其中最长的回文子序列,并返回该序列的长度。子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab" 输出:4 解释:一个可能的最长回文子序列为 "bbbb" 。示例 2:
输入:s = "cbbd" 输出:2 解释:一个可能的最长回文子序列为 "bb" 。提示:
1 <= s.length <= 1000
s
仅由小写英文字母组成
思路:
代码实现:
class Solution {
public int longestPalindromeSubseq(String s) {
// 创建dp表、初始化、填表、返回值
int n = s.length();
char[] ss = s.toCharArray();
int[][] dp = new int[n][n];
// 无需初始化
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] = 1;
} else if (i+1 == j) {
dp[i][j] = 2;
} else {
dp[i][j] = dp[i+1][j-1] + 2;
}
} else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
// 返回的是 [0,n-1] 区间内,最长子序列的长度
return dp[0][n-1];
}
}
1312.让字符串成为回文串的最少插入次数
题目:
给你一个字符串
s
,每一次操作你都可以在字符串的任意位置插入任意字符。请你返回让
s
成为回文串的 最少操作次数 。「回文串」是正读和反读都相同的字符串。
示例 1:
输入:s = "zzazz" 输出:0 解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。示例 2:
输入:s = "mbadm" 输出:2 解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。示例 3:
输入:s = "leetcode" 输出:5 解释:插入 5 个字符后字符串变为 "leetcodocteel" 。提示:
1 <= s.length <= 500
s
中所有字符都是小写字母。
思路:
代码实现:
class Solution {
public int minInsertions(String s) {
// 创建dp表、初始化、填表、返回值
int n = s.length();
char[] ss = s.toCharArray();
int[][] dp = new int[n][n];
// 无需初始化
// 由下到上、由左到右
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] = 0;
} else if (i+1 == j) {
dp[i][j] = 0;
} else {
dp[i][j] = dp[i+1][j-1];
}
} else {
dp[i][j] = Math.min(dp[i+1][j], dp[i][j-1]) + 1;
}
}
}
// 求整个字符串的变成回文串的最少操作次数
return dp[0][n-1];
}
}
好啦!本期 动态规划 —— 回文串问题 的刷题之旅 就到此结束啦!我们下一期再一起学习吧!