一、题目打卡
1.1 最长递增子序列
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size() <= 1) return 1; //题目写明:1 <= nums.length <= 2500
vector<int> dp(nums.size(), 1);
int res = INT_MIN;
// 以nums【i】为结尾的最长递增子序列的长度可以由 nums【0】为结尾的最长递增子序列长度、nums[1为结尾的最长长度、……nums【i-1】为结尾的最长长度 比较得到
for(int i = 1; i < nums.size();i++){
for(int j = 0; j < i;j++){
if(nums[i] > nums[j]) dp[i] = max(dp[j] + 1, dp[i]);
res = max(dp[i], res); // 如果后面出现了更小的数字,那后面的dp数组是会更小的
}
}
// for(auto &it :dp){
// cout << it <<" ";
// }
// cout << endl;
// return dp[nums.size() -1];
return res;
}
};
看到了一句评论,觉得总结的特别到位:
以nums【i】为结尾的最长递增子序列的长度可以由 nums【0】为结尾的最长递增子序列长度、nums[1为结尾的最长长度、……nums【i-1】为结尾的最长长度比较得到。
这里状态转移方程,需要一个比最大值的操作,因为存在第二层循环,其实也就是相当于第二次循环每次找到满足要求的,都进行相应的迭代。
基本的思路也就是这样,注意一下dp数组并没有严格的单调性,所以这里返回的结果需要另外维护一个变量,但是这样其实时间复杂度挺高的,第一步遍历dp数组肯定是无法简化的,而遍历j的过程,其实可以用一个单调栈然后二分来降低一点复杂度,具体的代码:
# Dynamic programming + Dichotomy.
class Solution:
def lengthOfLIS(self, nums: [int]) -> int:
tails, res = [0] * len(nums), 0
for num in nums:
i, j = 0, res
while i < j:
m = (i + j) // 2
if tails[m] < num: i = m + 1 # 如果要求非严格递增,将此行 '<' 改为 '<=' 即可。
else: j = m
tails[i] = num
if j == res: res += 1
return res
1.2 最长连续子序列
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
if(nums.size() <= 1) return 1;
vector<int> dp(nums.size(), 1);
int res = 0;
for(int i = 1; i < nums.size();i++){
if(nums[i] > nums[i-1]) dp[i] = max(dp[i], dp[i - 1] + 1);
res = max(res, dp[i]);
}
return res;
}
};
感觉这个题目比上一个题目要好理解一点,因为要求连续,所以就不存在再去遍历 j 到 i 的区间,只需要比较当前和前一位的一个大小情况就可以,思路也就直接地多。
1.3 最长重复子数组
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
vector<vector<int>> dp(nums1.size(), vector<int>(nums2.size(), 0));
int res = 0;
for(int i = 0 ; i < nums1.size();i++){
if(nums1[i] == nums2[0]){
dp[i][0] = 1;
res = 1;
}
}
for(int i = 0 ; i < nums2.size();i++){
if(nums2[i] == nums1[0]){
dp[0][i] = 1;
res = 1; //这里的res也一定要初始化
}
}
for(int i = 1; i < nums1.size();i++){
for(int j = 1 ; j < nums2.size(); j++){
if(nums1[i] == nums2[j]) dp[i][j] = dp[i-1][j-1] + 1;
res = max(res, dp[i][j]);
}
}
return res;
}
};
这个题目的状态定义和之前的差不多,不过注意题目是子数组而不是子序列,所以要求必须要连续,这种情况下,其实递推的过程就会稍微简单一点,如果画了表格来表示的话,其实就是从左上角的那个位置转移过来的。