动态规划2

106 阅读3分钟

基本概念

子序列是不要求连续的

子数组和子串一样,是需要连续的

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;
    }