647. 回文子串
题目讲解:代码随想录
重点:
- 理解dp数组的含义及递推公式。
思路:
- dp数组的含义
// 子串[i,j]是否是回文子串, 如果是dp[i][j]为true, 否则为false boolean[][] dp = new boolean[sArray.length][sArray.length];
- 递推公式
// s[i]与s[j]相等, 分三种情况 if (sArray[i] == sArray[j]) { // 第一种, 是同一个字符 if (j == i) { dp[i][j] = true; result++; // 第二种, 两个字符相邻 } else if (j - i == 1) { dp[i][j] = true; result++; // 第三种, 两个字符间隔一个或多个字符, 看内部子串是否为回文子串 } else { if (dp[i + 1][j - 1]) { dp[i][j] = true; result++; } } }
- dp数组的初始化
// 第一个字符和它本身肯定为回文子串 dp[0][0] = true;
- 遍历顺序
// 递推公式用到了dp[i + 1][j - 1], 在左下角, 所以先遍历列, 再遍历行 for (int j = 0; j < sArray.length; j++) for (int i = 0; i < sArray.length; i++)
- 模拟dp数组
public int countSubstrings(String s) {
int result = 0;
char[] sArray = s.toCharArray();
// 子串[i,j]是否是回文子串, 如果是dp[i][j]为true, 否则为false
boolean[][] dp = new boolean[sArray.length][sArray.length];
// 第一个字符和它本身肯定为回文子串
dp[0][0] = true;
// 递推公式用到了dp[i + 1][j - 1], 在左下角, 所以先遍历列, 再遍历行
for (int j = 0; j < sArray.length; j++) {
for (int i = 0; i < sArray.length; i++) {
if (i > j) break;
// s[i]与s[j]相等, 分三种情况
if (sArray[i] == sArray[j]) {
// 第一种, 是同一个字符
if (j == i) {
dp[i][j] = true;
result++;
// 第二种, 两个字符相邻
} else if (j - i == 1) {
dp[i][j] = true;
result++;
// 第三种, 两个字符间隔一个或多个字符, 看内部子串是否为回文子串
} else {
if (dp[i + 1][j - 1]) {
dp[i][j] = true;
result++;
}
}
}
}
}
return result;
}
516. 最长回文子序列
题目讲解:代码随想录
重点:
- 理解dp数组的含义及递推公式。
思路:
- dp数组的含义
// 字符串s在[i,j]范围内最长的回文子序列的长度为dp[i][j] int[][] dp = new int[sArray.length][sArray.length];
- 递推公式
// 当前字符相等, 内部最长回文加2 if (sArray[i] == sArray[j]) dp[i][j] = dp[i + 1][j - 1] + 2; // 当前字符不相等, 取 不取i 和 不取j 的最大值 else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
- dp数组的初始化
// 只有字符本身自己, 所以长度为1 for (int i = 0; i < sArray.length; i++) dp[i][i] = 1;
- 遍历顺序
// 从下往上, 从左往右遍历 for (int i = sArray.length - 1; i >= 0; i--) for (int j = i + 1; j < sArray.length; j++)
- 模拟dp数组
public int longestPalindromeSubseq(String s) {
char[] sArray = s.toCharArray();
// 字符串s在[i,j]范围内最长的回文子序列的长度为dp[i][j]
int[][] dp = new int[sArray.length][sArray.length];
// 只有字符本身自己, 所以长度为1
for (int i = 0; i < sArray.length; i++) dp[i][i] = 1;
// 从下往上, 从左往右遍历
for (int i = sArray.length - 1; i >= 0; i--) {
for (int j = i + 1; j < sArray.length; j++) {
// 当前字符相等, 内部最长回文加2
if (sArray[i] == sArray[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
// 当前字符不相等, 取 不取i 和 不取j 的最大值
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][sArray.length - 1];
}
动态规划总结
总结讲解:代码随想录
动规五部曲
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组动规结束语