题目:跳转至 321. 拼接最大数
给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。
示例 1:
输入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
输出:
[9, 8, 6, 5, 3]
示例 2:
输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]
示例 3:
输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
}
};
思路:
看题解,刚开始还没太明白为什么要单调栈以及怎么维护,看懂了还是夸赞。要获取单个序列nums中k个数量的最大(以原顺序)序列,计算nums的长度与k的差值,从左往右遍历nums,如果单调栈中有值并且栈顶值小于当前遍历值,且nums还有值,就把栈顶值和差值都减1,如果栈头没满,进栈,满了就差值减1。
写的有点乱,举个例子吧,序列nums为[9, 1, 2, 5, 8, 3, 1],取k=3时,单调栈中应该如下:[9,0,0]->[9,1,0]->[9,2,0]->[9,5,0]->[9,8,0]->[9,8,3]->[9,8,3]
理解了单调栈,再来看题目,两个序列按原顺序合并取出长度为k的最大数,那么要从两个序列中获取各自的最大子序列再进行合并(两个序列按原顺序比较大小合并这个比较简单),就要确认两个序列中分别要取出的个数,就得把所有可能循环。每个序列中能得出的子序列数目必然大于等于0,小于等于k,同时起点大于等于k与另一个序列长度的差值,终点小于等于k。比较所有可能结果获取最大值。
写完提交提示错误,查看发现把合并想简单了,不能简单用双指针判断两个序列当前值的大小比较合并,因为他们是按原序列中的顺序排列的。继续看解答代码,合并函数中也用到比较函数,好厉害,也对,比较函数本来就是比较两个序列大小,只不过按上述思路只要求比较同等数量下的序列大小,现在多加两个索引,比较从索引开始的序列大小。
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
int len1=nums1.size();
int len2=nums2.size();
int start=max(0,k-len2);
int end=min(k,len1);
vector<int> res;
for(int i=start;i<=end;++i){
vector<int> maxseq1=maxSequence(nums1,i);
vector<int> maxseq2=maxSequence(nums2,k-i);
vector<int> meargeseq=meargeSequence(maxseq1,maxseq2);
if(res.size()==0)
res=meargeseq;
else
res=compareSequence(res,0,meargeseq,0)>0?res:meargeseq;
}
return res;
}
vector<int> maxSequence(vector<int>& nums, int k){
int len=nums.size();
vector<int> res(k,0); //初始化单调栈
int top=-1;
int remain=len-k;
for(int i=0;i<len;++i){
int num=nums[i];
while(top>=0 && res[top]<num && remain>0){ //维护最大序列
--top;
--remain;
}
if(top<k-1)
res[++top]=num; //栈没满时才进栈
else
--remain; //栈满时当前值未被用到,序列剩余可用值-1
}
return res;
}
vector<int> meargeSequence(vector<int>& nums1, vector<int>& nums2){
int len1=nums1.size();
int len2=nums2.size();
vector<int> res(len1+len2,0);
int index1=0,index2=0;
while(index1<len1 && index2<len2){
if(compareSequence(nums1,index1,nums2,index2)>0){
res[index1+index2]=nums1[index1];
++index1;
}
else{
res[index1+index2]=nums2[index2];
++index2;
}
}
while(index1<len1){
res[index1+index2]=nums1[index1];
++index1;
}
while(index2<len2){
res[index1+index2]=nums2[index2];
++index2;
}
return res;
}
int compareSequence(vector<int>& nums1,int index1,vector<int>& nums2,int index2){ //返回值大于0返回nums1
int len1=nums1.size();
int len2=nums2.size();
while(index1<len1 && index2<len2){
int difference=nums1[index1]-nums2[index2];
if(difference!=0)
return difference;
++index1;
++index2;
}
//return (len1 - index1) - (len2 - index2);
//上面是题解的直接没太懂,换个写法,如果一方值用完还相等,返回先用完的是不对的,因为单调递减,所以先用完的最后一个值也是小于另一个序列对应位置的之前的值
if(index1==len1)
return -1;
return 1;
}
};