1143.最长公共子序列
题目链接:1143. 最长公共子序列 - 力扣(Leetcode)
解题思路:
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
/**
* 1.dp[i][j] test1 0-i字符 test2 0-j字符 的最长公共子序列
* 2.if(test1.charAt(i) == test2.charAt(j)) dp[i][j] = dp[i - 1][j - 1] + 1;
* else dp[i][j] = Math.max(dp[i, j - 1],dp[i - 1][j]);
* 3.初始化
*/
int[][] dp = new int[text1.length()][text2.length()];
int rcd = 0;
char c2 = text2.charAt(0);
for (int i = 0; i < text1.length(); i++) {
if(c2 == text1.charAt(i) && rcd == 0) rcd = 1;
dp[i][0] = rcd;
}
rcd = 0;
char c1 = text1.charAt(0);
for (int j = 0; j < text2.length(); j++) {
if (c1 == text2.charAt(j) && rcd == 0) rcd = 1;
dp[0][j] = rcd;
}
for(int i = 1; i < text1.length(); i++){
for(int j = 1; j < text2.length(); j++){
if(text1.charAt(i) == text2.charAt(j)) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = Math.max(dp[i][j - 1],dp[i - 1][j]);
}
}
return dp[text1.length() - 1][text2.length() - 1];
}
}
1035.不相交的线
题目链接:1035. 不相交的线 - 力扣(Leetcode)
解题思路:这个题就是最长公共子序列的一个变种。
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
/**
* 1.dp[i][j] 以nums1 0-i这段数组和nums2 0-j这段数组的最多不相交的线
* 2.if(nums1[i] == nums2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
* else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
* 3.从第一次匹配的位置为分割点,前面是0,后面是1
*/
int[][] dp = new int[nums1.length][nums2.length];
int rcd = 0;
for(int i = 0; i < nums1.length; i++){
if(rcd == 0 && nums1[i] == nums2[0]) rcd = 1;
dp[i][0] = rcd;
}
rcd = 0;
for(int i = 0; i < nums2.length; i++){
if(rcd == 0 && nums2[i] == nums1[0]) rcd = 1;
dp[0][i] = rcd;
}
for(int i = 1; i < nums1.length; i++){
for(int j = 1; j < nums2.length; j++){
if(nums1[i] == nums2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[nums1.length - 1][nums2.length - 1];
}
}
53.最大子数组和
题目链接:53. 最大子数组和 - 力扣(Leetcode)
解题思路:
因为本题目要求是最大和的连续子数组,所以,dp数组应该定义为以元素i为结尾的连续子数组的最大和
然后再通过一个ans记录从0-i元素结尾的各个最大和的最大和。
class Solution {
public int maxSubArray(int[] nums) {
/**
* 1.dp[i] 以元素i结尾的具有最大和的连续子数组
* 两种可能,以起始点区分开,1.起始点就是i 2.起始点不是i
* 2.dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
* 3.dp[0] = nums[0]
*/
int[] dp = new int[nums.length];
int ans = nums[0];
dp[0] = nums[0];
for(int i = 1; i < nums.length; i++){
dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
ans = Math.max(ans, dp[i]);
}
return ans;
}
}
总结
这种公共子序列和公共子数组题目的dp数组定义可以分为两类
一类是需要子数组或者子序列连续,一类不要求子数组和子序列连续
如果要求连续,就类似53题这样,以每个元素为结尾去定义dp数组,然后求这么多种情况中的极值。
/**
* 1.dp[i] 以元素i结尾的具有最大和的连续子数组
* 两种可能,以起始点区分开,1.起始点就是i 2.起始点不是i
* 2.dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
* 3.dp[0] = nums[0]
*/
如果不要求连续,就以例如如下方式去定义dp。结果就是dp的最后推出来的元素。就不需要求极值了。
/**
* 1.dp[i][j] 以nums1 0-i这段数组和nums2 0-j这段数组的最多不相交的线
* 2.if(nums1[i] == nums2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
* else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
* 3.从第一次匹配的位置为分割点,前面是0,后面是1
*/