给你两个下标从 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]供着一轮的状态转移使用。