最长递增子序列
这里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]。要额外避免数组越界。