LeetCode 647 回文子串
思路
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
考虑动态规划五部曲:
- dp数组及其下标含义:为了方便找到递归关系,考虑回文串的性质有:如果串
s[i+1, j-1]是回文串,当s[i]==s[j],s[i, j]也是回文串。有i,j两个维度,所以需要定义一个二维dp数组。那么dp[i][j]的含义就是,s[i,j]是否是回文串。最后统计数组中有几个回文串就可以得到返回结果。 - 确定递推公式:
if (s[i]==s[j]) dp[i][j] = dp[i+1][j-1]如果j-i <= 1,直接等于true。if (s[i]!=s[j]) dp[i][j] = false
- dp数组如何初始化:根据递推公式和定义,
dp[i][j]以赖于左下角的元素,而i>j的元素是没有意义的。所以初始化所有i==j的元素 - 确定遍历顺序:为了让左下角一直先被填写。需要从左到右,从下到上遍历。所以i的初始值以赖于j,把j放在外层,i放在内层。
- 举例推导dp数组
解法
class Solution {
public int countSubstrings(String s) {
boolean[][] dp = new boolean[s.length()][s.length()];
for (int i = 0; i < s.length(); i++) {
dp[i][i] = true;
}
int count = s.length();
for (int j = 0; j < s.length(); j++) {
for (int i = j - 1; i >= 0; i--) {
if (s.charAt(i) == s.charAt(j)) {
if (j - i <= 1) {
dp[i][j] = true;
}
else {
dp[i][j] = dp[i+1][j-1];
}
}
else {
dp[i][j] = false;
}
if (dp[i][j]) {
count++;
}
}
}
return count;
}
}
LeetCode 516 最长回文子序列
思路
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
本题找的是回文子序列。由于不要求连续,我们可以把目标返回值作为数组的定义。
考虑动态规划五部曲:
- dp数组及其下标含义:
dp[i][j]表示s[i,j]子串中最长回文子序列的长度 - 确定递推公式:
if (s[i]==s[j]) dp[i][j] = dp[i+1][j-1] + 2.如果j - i <= 1,dp[i][j] = j - i + 1,即s[i, j]的长度if (s[i]!=s[j]) dp[i][j] = max(dp[i][j-1], dp[i-1][j])
- dp数组如何初始化:都初始化为0
- 确定遍历顺序:根据递推公式,一个元素依赖于其左下角的元素,左边的和下面的元素。所以需要从左到右,从下到上遍历。
- 举例推导dp数组
解法
class Solution {
public int longestPalindromeSubseq(String s) {
int[][] dp = new int[s.length()][s.length()];
for (int j = 0; j < s.length(); j++) {
for (int i = j; i >= 0; i--) {
if (s.charAt(i)==s.charAt(j)) {
if (j - i > 1) {
dp[i][j] = dp[i+1][j-1] + 2;
}
else {
dp[i][j] = j - i + 1;
}
}
else {
dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
}
}
}
return dp[0][s.length()-1];
}
}
动态规划总结
动态规划五部曲
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
动态规划基础
主要围绕动态规划的基础方法论
背包问题
股票问题
打家劫舍问题
子序列问题
今日收获总结
今日学习三小时。总结见动态规划大总结。