给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,长度均为 n 。
让我们定义另一个下标从 0 开始、长度为 n 的整数数组,nums3 。对于范围 [0, n - 1] 的每个下标 i ,你可以将 nums1[i] 或 nums2[i] 的值赋给 nums3[i] 。
你的任务是使用最优策略为 nums3 赋值,以最大化 nums3 中 最长非递减子数组 的长度。
以整数形式表示并返回 nums3 中 最长非递减 子数组的长度。
注意:子数组 是数组中的一个连续非空元素序列。
示例 1:
输入:nums1 = [2,3,1], nums2 = [1,2,1]
输出:2
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums2[1], nums2[2]] => [2,2,1]
从下标 0 开始到下标 1 结束,形成了一个长度为 2 的非递减子数组 [2,2] 。
可以证明 2 是可达到的最大长度。
示例 2:
输入:nums1 = [1,3,2,1], nums2 = [2,2,3,4]
输出:4
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums2[1], nums2[2], nums2[3]] => [1,2,3,4]
整个数组形成了一个长度为 4 的非递减子数组,并且是可达到的最大长度。
示例 3:
输入:nums1 = [1,1], nums2 = [2,2]
输出:2
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums1[1]] => [1,1]
整个数组形成了一个长度为 2 的非递减子数组,并且是可达到的最大长度。
记忆化搜索
class Solution {
public:
int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
//记忆化搜索
int n = nums1.size();
int memo[n][2];
memset(memo, -1, sizeof(memo));
vector<vector<int>> nums(2, vector<int>(n));
nums[0] = {nums1.begin(), nums1.end()};
nums[1] = {nums2.begin(), nums2.end()};
function<int(int, int)> dfs = [&](int i, int j) -> int{
if(i == 0) return 1;
if(memo[i][j] != -1) return memo[i][j];
memo[i][j] = 1;
if(nums1[i-1] <= nums[j][i]) memo[i][j] = dfs(i-1, 0) + 1;
if(nums2[i-1] <= nums[j][i]) memo[i][j] = max(memo[i][j], dfs(i-1, 1) + 1);
return memo[i][j];
};
int mx = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < 2; j++){
mx = max(mx, dfs(i, j));
}
}
return mx;
}
};
我们定义dfs(i, j)为以nums[j][i]结尾的最长非递减子序列长度。
为了在记忆化搜索里面更好地进行递归,我们定义nums[0][i]为nums1,nums[1][j]为nums[2]。
在记忆化搜索的递归函数中,我们定义当i = 0的时候,记忆化搜索终止,返回1。并且我们通过memo[i][j]来记录已经计算过的值,如果memo[i][j]不为-1,说明之前计算过,直接返回即可。
然后如果这时候i既不是0,并且没有计算过dfs(i, j)的值,那么我们就令memo[i][j]先为1,这样即使后面两个if都不通过,也能返回一个正确的值。
然后我们这时候进行判断,看我们现在nums[j][i]与nums1[i-1]和nums2[i-1]进行比较,如果大于等于他们,则说明我们这时候的最大非递减子序列长度就是要么dfs(i-1, 0)+1,要么就是dfs(i-1, 1)+1,取两者最大值即可。
在最后,我们遍历每个nums1和nums2数组的元素,将它作为最长非递减子序列的末尾元素,然后用变量mx来记录最大长度。
翻译成递推
class Solution {
public:
int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(),f[n][2];
f[0][0] = f[0][1] = 1;
vector<vector<int>> nums(2,vector<int>(n));
nums[0] = {nums1.begin(),nums1.end()};
nums[1] = {nums2.begin(),nums2.end()};
int mx = 1;
for(int i = 1;i < n; i++){
for(int j = 0;j < 2 ;j++){
f[i][j] = 1;
if(nums1[i - 1] <= nums[j][i]) f[i][j] = f[i - 1][0] + 1;
if(nums2[i - 1] <= nums[j][i]) f[i][j] = max(f[i][j],f[i - 1][1] + 1);
mx = max(mx,f[i][j]);
}
}
return mx;
}
};
我们也可以将记忆化搜索1:1翻译成递推的形式。
空间优化
class Solution {
public:
int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(),f[2];
f[0] = f[1] = 1;
vector<vector<int>> nums(2,vector<int>(n));
nums[0] = {nums1.begin(),nums1.end()};
nums[1] = {nums2.begin(),nums2.end()};
int mx = 1;
for(int i = 1;i < n; i++){
int t0 = f[0], t1 = f[1];
for(int j = 0;j < 2 ;j++){
f[j] = 1;
if(nums1[i - 1] <= nums[j][i]) f[j] = t0 + 1;
if(nums2[i - 1] <= nums[j][i]) f[j] = max(f[j],t1 + 1);
mx = max(mx,f[j]);
}
}
return mx;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
我们可以对递推代码进行空间优化,在这里f[i][j] = f[i - 1][0] + 1
以及f[i][j] = max(f[i][j],f[i - 1][1] + 1);
,f[i][j]都是由上一轮i-1转换而来,由于我们在使用一位数组的时候,会存在覆盖,使f[j]变成这一轮计算过的值而不是上一轮,所以我们在每一轮开始的时候,通过两个变量t0和t1来记录上一轮的f[0]和f[1]供着一轮的状态转移使用。