LeetCode 1143 最长公共子序列
题目链接:https://leetcode.cn/problems/longest-common-subsequence/
思路:
-
dp数组的含义
dp[i][j]代表以i-1结尾的text1数组和以j-1结尾的text2数组最长的重复子数组大小 -
递推公式
本题有两种情况:
1、text1[i - 1] == text2[j - 1]
所以此时递推公式为:
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1]+1 dp[i][j]=dp[i−1][j−1]+1
2、text1[i - 1] != text2[j - 1]
例子:text1:abc text2:ace
两种情况:
因为c和e不相同,所以可以是abc和ac相比,得出公共子序列的长度,也可以是ab和ace相比
所以此时递推公式是:
d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) dp[i][j] = max(dp[i][j-1],dp[i-1][j]) dp[i][j]=max(dp[i][j−1],dp[i−1][j]) -
初始化
dp[i][0]和dp[0][j]显然都是没有意义的,即二维数组的第一行和第一列,将其全部初始化为0即可。其余数值因为会在递推公式中被覆盖,所以也都初始化为0,这样可以使得代码相对简洁。 -
遍历顺序
显然遍历是从前往后,从左到右
代码:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>>dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
for(int i = 1; i <= text1.size(); i++)
{
for(int j = 1; j <= text2.size(); j++)
{
if(text1[i - 1] == text2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
// for(int i = 0; i < dp.size(); i++)
// {
// for(int j = 0; j < dp[0].size(); j++)
// cout << dp[i][j] << " ";
// cout<< endl;
// }
// cout << endl;
return dp[text1.size()][text2.size()];
}
};
总结
对比最大重复子数组,本题少了个限制就是不需要是连续的。
LeetCode 1035 不相交的线
题目链接:https://leetcode.cn/problems/uncrossed-lines/
思路:
本题其实和上一题最大公共子序列是一样的,因为连线不能相交,代表了只能一直往后连,即不能改变相对两个数组的相对顺序。
代码:
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
vector<vector<int>>dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));
for(int i = 1; i <= nums1.size(); i++)
{
for(int j = 1; j <= nums2.size(); j++)
{
if(nums1[i - 1] == nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[nums1.size()][nums2.size()];
}
};
总结
能看出来和上一题一样就不难了
LeetCode 53 最大子序和
题目链接:https://leetcode.cn/problems/maximum-subarray/
思路:
-
dp数组的含义
dp[i]代表包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。 -
递推公式
显然,有递推公式: d p [ i ] = d p [ i − 1 ] + n u m s [ i ] dp[i]=dp[i-1]+nums[i] dp[i]=dp[i−1]+nums[i]
如果从0开始的连续子序列和小于当前的nums[i],那么 d p [ i ] = n u m s [ i ] dp[i]=nums[i] dp[i]=nums[i],这样可以从当前位置开始重新往后计算子序和。
所以,递推公式为:
d p [ i ] = m a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) dp[i]=max(dp[i-1]+nums[i],nums[i]) dp[i]=max(dp[i−1]+nums[i],nums[i])
例子:[-2,1,-3,4,-1,2,1,-5,4]
在 n u m s [ 3 ] = 4 nums[3]=4 nums[3]=4前, d p [ 0 ] = − 2 , d p [ 1 ] = − 1 , d p [ 2 ] = − 2 dp[0]=-2,dp[1]=-1,dp[2]=-2 dp[0]=−2,dp[1]=−1,dp[2]=−2,显然此时4前面的数加起来后的子序和小于当前数组值即4,所以应该从当前数组值开始重新往后计算子序和。 -
初始化
显然,由递推公式可知,需要知道dp[0]的值,所以 d p [ 0 ] = n u m s [ 0 ] dp[0]=nums[0] dp[0]=nums[0] -
遍历顺序
从前往后
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == 1) return nums[0];
vector<int>dp(nums.size(), 0);
dp[0] = nums[0];
int result = dp[0];
for(int i = 1; i < nums.size(); i++)
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
result = result > dp[i] ? result : dp[i];
}
return result;
}
};
总结
要想清楚如何重新开始计算子序和
今日总结:
继续学习子序列问题。