最长递增子序列
这里dp数组的定义比较难想。dp[i] 表示 i 之前的序列中以 nums[i] 结尾的最长递增子序列长度,只有这样才能实现状态递推。对于dp数组初始化,所有下标位置都应该初始化为1。
class Solution{
public:int lengthOfLIS(vector<int>& nums) {vector<int> dp(nums.size(), 1);int result = 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[i], dp[j] + 1);}if(dp[i] > result){ // 维护dp数组中的最大值作为最终结果,每一个下标都有可能作为子序列的结尾result = dp[i];}}return result;}
};
最长连续递增序列
这道题不同于上一道题,要求必须是连续子序列,这样当前状态只与上一状态有关,而不连续子序列中的状态则与 0 ~ i-1 的状态都相关。
class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {// dp[i]表示i之前的序列中以nums[i]结尾的连续递增子序列的最长长度vector<int> dp(nums.size(), 1);int result = 1;for(int i = 1; i < nums.size(); i++) {if(nums[i] > nums[i - 1])dp[i] = dp[i - 1] + 1;if(dp[i] > result) result = dp[i];}return result;}
};
其实这道题贪心也能解,而且具有O(1)的复杂度。
class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {int count = 1;int result = 1;for(int i = 1; i < nums.size(); i++) {if(nums[i] > nums[i - 1]){ // 局部连续递增,count加一count++;}else{count = 1; // 连续递增结束,count = 1}if(count > result) result = count; // 局部最优解取最大值,得到全局最优}return result;}
};
最长重复子数组
使用一个二维dp数组就可以覆盖所有的数组比较情况。dp[i][j] 表示数组1以 i-1 下标结尾的子数组与数组2以 j-1 下标结尾的子数组中最长的公共子数组长度。注意:这里之所以表示i-1和j-1下标是为了初始化的方便,代码可以更加简洁,所以就在dp数组含义的定义上绕了一下。
如果定义i-1,那么在遍历递推时肯定要从 i=1, j=1 开始,dp[0][j]和dp[i][0]实际实际上都是没有意义的。只是为了后面递推结果的正确,假设nums[i - 1] == nums2[0]
,则 dp[i][1] = dp[i - 1][0] + 1,这里dp[i - 1][0] 只能为0。
class Solution{
public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1, 0));int result = 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;}if(result < dp[i][j]){result = dp[i][j];}}}return result;}
};
如果dp数组的含义就是直接表示以 i 或 j 结尾的子数组之间的数值,那么需要进行额外的初始化,而且在遍历递推时仍然要从0开始,因为要统计最大值求得最终结果,需要包含 dp[i][0] 和 dp[0][j]。要额外避免数组越界。