基本概念
子序列是不要求连续的
子数组和子串一样,是需要连续的
0. 抢劫问题
一维数组,没什么好说的,热身题。
核心点:当前i这个到底抢还是不抢,然后获取一个最大值。
dp[i] = Math.max(dp[i-2] + nums[i], dp[i-2])
1. 最长连续递增子序列的长度 (等同于子数组,子串)
子数组,子串,必须是连续的。依然是先遍历。然后j再次遍历。
核心点:
dp[i] --> 以第i个元素结尾时,组成的最长连续递增子序列的长度。
if (nums[i] > nums[i-1]) {
dp[i] = dp[i-1] + 1
}
连续的只能和前一个进行比较。
2. 最长递增子序列的长度
子序列,不连续的。for循环遍历的时候,先有第一层遍历。然后引出了第二层遍历。
核心点:
dp[i]的定义:以第i个元素结尾时,递增子序列的长度。
dp[i] = Math.max(dp[j] + 1, dp[i])
不连续的和之前所有的进行比较
3. 最长公共子数组 (二维数组)
公共子数组,首先对两个数组循环遍历。开始设计道二维数组了。二维数组,可以对一个进行固定,然后遍历第二个数组。
核心点
dp[i][j] ----> 以i结尾的nums1和以j结尾的nums[2]的最长子数组的长度。
dp[i][j] = dp[i-1][j-1] + 1
这个不一定是dp[i][j] 最大。
4. 最长公共子序列 (二维数组)
公共子序列,依然对两个数组进行遍历。之前只需要考虑相等的情况,现在需要考虑不等的情况。
核心点
dp[i][j] -----> 以i结尾的数组1和以j结尾的数组2所构成的最长子序列的长度。
if (nums[i] == nums[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
一定是最后dp[i][j]最大。
6. 回文子串的个数 (二维数组,而且是boolean数组)
有点类似最长递增子序列。先对字符串进行遍历,然后对字符串的0到i再次进行遍历,只不过是倒序遍历。
核心点
dp[i][j] ----> 以i开头,以j结尾的时候回文串的个数。
dp[i][j] = dp[i + 1][j-1] -----> 之前都是i-1 ,所以都是正序,这个是倒序。
if(s.charAt(i) == s.charAt(j)) {
if (j - i < = 1) {
res ++
dp[i][j] = true
} else {
if (dp[i+1][j-1]) {
res ++
dp[i][j] = true
}
}
}
最后统计res的个数即可。
还有一种解法是双指针,i和j,这个可能更好理解一些。
7. 回文子序列的长度 (二维数组,int类型)
上一个题是求个数,这个题是求长度了。因此就变成int类型了,依然是倒序遍历。 上一个题只考虑了想等的情况,这个题需要考虑不想等的情况,因为动态规划都是由前者推导出后者。
核心点:
dp[i][j] ----> 以i开头,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][length-1]
8. 编辑距离 (二维数组)
困难级别题目,构建二维数组。 如果是 nums[i] == nums[j] ----> dp[i][j] = dp[i-1][j-1] 否则 增加,删除,修改。
核心点
1. 从1开始遍历,需要解决0的问题
2. length 要 + 1 (这个要一直没想明白!!)
3. if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1;
}