随想录日记part42
t i m e : time: time: 2024.04.14
主要内容:今天开始要学习动态规划的相关知识了,今天的内容主要涉及:最长递增子序列 ;最长连续递增序列 ;最长重复子数组 ;最长公共子序列;不相交的线 ;最大子序和动态规划
- 300.最长递增子序列
- 674. 最长连续递增序列
- 718. 最长重复子数组
- 1143.最长公共子序列
- 1035.不相交的线
- 53. 最大子序和动态规划
动态规划五部曲:
【1】.确定dp数组以及下标的含义
【2】.确定递推公式
【3】.dp数组如何初始化
【4】.确定遍历顺序
【5】.举例推导dp数组
Topic1最长递增子序列
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
2.确定递推公式:
//位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。
if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
3.dp数组如何初始化
Arrays.fill(dp, 1);
4.确定遍历顺序
从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。
5.举例推导dp数组
输入:[0,1,0,3,2],dp数组的变化如下:
代码如下:
class Solution {
public int lengthOfLIS(int[] nums) {
int len = nums.length;
// dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
int[] dp = new int[len];
Arrays.fill(dp, 1);
if (len == 1)
return 1;
dp[0] = 1;
int max_value = 1;
for (int i = 1; i < len; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j])
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
max_value = Math.max(max_value, dp[i]);
}
return max_value;
}
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
)
O(n)
O(n)
Topic2最长连续递增序列
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
2.确定递推公式:
if(nums[i]>nums[i-1])dp[i]=dp[i-1]+1;
3.dp数组如何初始化
Arrays.fill(dp, 1);
4.确定遍历顺序
从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。
5.举例推导dp数组
已输入nums = [1,3,5,4,7]为例,dp数组状态如下:
class Solution {
public int findLengthOfLCIS(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
Arrays.fill(dp, 1);
int result = 1;
for (int i = 1; i < len; i++) {
if (nums[i] > nums[i - 1]) {
dp[i] = dp[i - 1] + 1;
}
result = Math.max(result, dp[i]);
}
return result;
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
Topic3最长重复子数组
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
2.确定递推公式:
根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;根据递推公式可以看出,遍历i 和 j 要从1开始!
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;
for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
拿示例1中,A: [1,2,3,2,1],B: [3,2,1,4,7]为例,画一个dp数组的状态变化,如下:
class Solution {
public int findLength(int[] nums1, int[] nums2) {
// dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i -1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for (int i = 0; i <= nums1.length; i++)
dp[i][0] = 0;
for (int i = 0; i <= nums2.length; i++)
dp[0][i] = 0;
int result = 0;
for (int i = 1; i < nums1.length + 1; i++) {
for (int j = 1; j < nums2.length + 1; j++) {
if (nums1[i - 1] == nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
result = Math.max(result, dp[i][j]);
}
}
return result;
}
}
Topic4最长公共子序列
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
2.确定递推公式:
主要就是两大情况:1.text1[i - 1] 与 text2[j - 1]相同 2.text1[i - 1] 与 text2[j - 1]不相同
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;
for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
以输入:text1 = “abcde”, text2 = “ace” 为例,dp状态如图:
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int len1 = text1.length();
int len2 = text2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++)
dp[i][0] = 0;
for (int i = 0; i <= len2; i++)
dp[0][i] = 0;
int result = 0;
for (int i = 1; i < len1 + 1; i++) {
for (int j = 1; j < len2 + 1; j++) {
if (text1.charAt(i - 1) == text2.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
result = Math.max(result, dp[i][j]);
}
}
return result;
}
}
时间复杂度:
O
(
n
∗
m
)
O(n*m)
O(n∗m)
空间复杂度:
O
(
n
∗
m
)
O(n*m)
O(n∗m)
Topic5不相交的线
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
2.确定递推公式:
主要就是两大情况:1.text1[i - 1] 与 text2[j - 1]相同 2.text1[i - 1] 与 text2[j - 1]不相同
如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
3.dp数组如何初始化
for(int i=0;i<=nums1.length;i++) dp[i][0]=0;
for(int i=0;i<=nums2.length;i++) dp[0][i]=0;
4.确定遍历顺序
外层for循环遍历A,内层for循环遍历B。
5.举例推导dp数组
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++)
dp[i][0] = 0;
for (int i = 0; i <= len2; i++)
dp[0][i] = 0;
int result = 0;
for (int i = 1; i < len1 + 1; i++) {
for (int j = 1; j < len2 + 1; j++) {
if (nums1[i - 1] == nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
result = Math.max(result, dp[i][j]);
}
}
return result;
}
}
时间复杂度:
O
(
n
∗
m
)
O(n*m)
O(n∗m)
空间复杂度:
O
(
n
∗
m
)
O(n*m)
O(n∗m)
Topic6最大子序和动态规划
题目:
思路:
接下来进行动规五步曲:
1.确定dp数组以及下标的含义:
dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]
2.确定递推公式:
dp[i]只有两个方向可以推出来:
dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
nums[i],即:从头开始计算当前连续子序列和
一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]);
3.dp数组如何初始化
dp[0] = nums[0]
4.确定遍历顺序
递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。
5.举例推导dp数组
以示例一为例,输入:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
Arrays.fill(dp, Integer.MIN_VALUE);
int tem;
dp[0] = nums[0];
if (len == 0)
return 0;
int result = nums[0];
for (int i = 1; i < len; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
result = Math.max(result, dp[i]);
}
return result;
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)