1143.最长公共子序列
文章讲解:https://programmercarl.com/1143.%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E5%BA%8F%E5%88%97.html
题目链接:https://leetcode.cn/problems/longest-common-subsequence/
视频讲解:https://www.bilibili.com/video/BV1ye4y1L7CQ/
和最长重复子数组的处理逻辑一样。dp[i][j]定义为0到i-1,0到j-1的最长公共子序列。
这里的递推公式没有想明白,递推公式的第一个判断,nums[i-1]==nums[j-1]和最长重复子数组一样,都取dp[i][j] = dp[i-1][j-1] + 1。
但是当nums[i-1]!=nums[j-1]时,则取dp[i-1][j],dp[i][j-1]的最大值,dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]); 如给定 a b c d e;a c e;此时比到 abc和ace,发现c和e不相等,则取 abc和ac以及ab和ace的值的最大值。
这里的初始化值和最长重复子数组一样。
理解了思路,然后按照思路些代码还是简单的。
public int longestCommonSubsequence(String text1, String text2) {
char[] text1Array = text1.toCharArray();
char[] text2Array = text2.toCharArray();
// 定义dp数组,2层循环,dp[i][j],以i为结尾的text1Array和以j为结尾的text2Array组成的最大公共子序列长度
// 确定推导公式,前后有无递推 如果 text1Array[i] == text2Array[j],不是前一个相等了
int[][] dp = new int[text1Array.length + 1][text2Array.length + 1];
// dp数组定义 dp[i][j]:用nums1的i-1位置为结尾以及nums2的j-1为结尾的最长子数组长度
int result = 0;
for(int i = 1; i <= text1Array.length; i++){
for(int j = 1; j <= text2Array.length; j++){
// 推导公式,如果每个数字的位数都相等,则长度加一
if(text1Array[i - 1] == text2Array[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + 1;
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
result = Math.max(dp[i][j],result);
}
}
return result;
}
1035.不相交的线
文章讲解:https://programmercarl.com/1035.%E4%B8%8D%E7%9B%B8%E4%BA%A4%E7%9A%84%E7%BA%BF.html
题目链接:https://leetcode.cn/problems/uncrossed-lines/
视频讲解:https://www.bilibili.com/video/BV1h84y1x7MP/
// 不相交的线,满足顺序,但是可以不连续,题目处理的方法和最长公共子序列一致。
代码实现:
public int maxUncrossedLines(int[] nums1, int[] nums2) {
// 不相交的线,满足顺序,但是可以不连续
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
int result = Integer.MIN_VALUE;
for(int i = 1; i <= nums1.length; i++){
for(int j = 1; j <= nums2.length; 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 - 1][j],dp[i][j - 1]);
}
result = Math.max(result,dp[i][j]);
}
}
return result;
}
思路:
绘制一些连接两个数字 A[i] 和 B[j] 的直线,只要 A[i] == B[j],且直线不能相交!
直线不能相交,这就是说明在字符串A中 找到一个与字符串B相同的子序列,且这个子序列不能改变相对顺序,只要相对顺序不改变,链接相同数字的直线就不会相交。
还是要理解题目中的类似之处。
53. 最大子序和
文章讲解:https://programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html
题目链接:https://leetcode.cn/problems/maximum-subarray/
视频讲解:https://www.bilibili.com/video/BV19V4y1F7b5/
dp数组定义:
dp[i]:以i结尾的0-i最大子序和。
递推公式:dp[i-1] >= 0 则dp[i] = dp[i - 1] + nums[i] 否则dp[i] = nums[i]
初始化值:dp[0] = nums[0];
整体逻辑一样,将dp的取值直接使用Math.max(dp[i - 1] + nums[i], nums[i]);
也是将result的值初始化成了dp[0];
public int maxSubArray(int[] nums) {
// 动态规划实现
// dp数组定义:dp[i]:取0-i时的最大和的连续子数组
// 确定递推,dp[i-1] >= 0 则dp[i] = dp[i - 1] + nums[i] 否则dp[i] = nums[i]
// 初始化值,dp[0] = nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
if(nums.length == 1)return nums[0];
int result = dp[0];
for(int i = 1; i < nums.length; i++){
if(dp[i-1] >= 0){
dp[i] = dp[i - 1] + nums[i];
}else{
dp[i] = nums[i];
}
result = Math.max(result,dp[i]);
}
return result;
}
1143.最长公共子序列、1035.不相交的线、718. 最大子序和
前面两道题的答案都是一样的,第二道题要理解不相交的概念就是顺序匹配。
第一题、第二题的核心思路是如果当前位相同则取前一位置的最大值+1。不是当前位的话取不相等时的2种状态的最大值 abcde ace,比较到abc和ace时取abc-ac和ab-ace的最大值。
最大子序和的话就是取前一位置+当前位置和当前nums的最大值。