题目:
Given two arrays of length m
and n
with
digits 0-9
representing two numbers. Create the maximum number of length k
<= m + n
from digits of the two. The relative order of the digits from the same array must be preserved. Return an array of the k
digits.
You should try to optimize your time and space complexity.
Example 1:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
return [9, 8, 6, 5, 3]
Example 2:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
return [6, 7, 6, 0, 4]
Example 3:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
return [9, 8, 9]
思路:
我们分三步实现这一算法:1)计算出从nums1中取i个数字,以及从nums2中取k-i个字符分别所能构成的最大数;2)计算从nums1和nums2中所取的这k个数字所能构成的最大数;3)枚举i可能的取值,从中选取可以构成的最大数并返回。
首先我们需要从理论上证明:假设最终构成的最大数是从nums1中取了i个数,那么这i个数必然在nums1中可以形成最大的数(可以利用反证法证明,这里证明从略)。下面我们分别解释每一步的实现:
1)从nums1中选取i个数字,使得这i个数字构成的数最大。这实际上和Leetcode 316这道题目的思路一模一样,具体请见《[Leetcode] 316. Remove Duplicate Letters 解题报告》。可以采用贪心策略,一旦发现还可以删除数字,并且当前要加入的数字比结果集中最后一个数字要大,就把最后一个数字出栈。
2)基于nums1中的选出的这i个数字和nums2中选取出的这k-i个数字,构造最大数。这实际上就是merge sort的变种。每次选取nums1和nums2的头部的最大数并加入结果集。需要注意的是,如果两个头部的数字相等,那么还需要看第二位,如果第二位相同,则还需要看第三位……幸运的是vector<int>的默认比较函数实现的正是这一功能。
3)枚举所有i的可能值,计算可以构成的最大数。
总之,感到这道题目还是挺难的,考查的知识点除了贪心策略,还有归并排序等,我觉得要在面试现场写出bug free的代码还挺不容易的。
代码:
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
vector<int> result;
int len1 = nums1.size(), len2 = nums2.size();
for(int i = max(k - len2, 0); i <= min(k, len1); ++i) {
// get i digits from nums1, and get (k-i) digits from nums2
result = max(result, merge(getMax(nums1, i), getMax(nums2, k-i)));
}
return result;
}
private:
vector<int> getMax(vector<int> &nums, int k) { // We use greedy algorithm to get the Max vector<int>
vector<int> vec;
int size = nums.size();
int drop = size - k;
for(int i = 0; i < nums.size(); ++i) {
while(drop > 0 && vec.size() > 0 && nums[i] > vec.back()) {
--drop;
vec.pop_back(); // drop the last element from the vec
}
vec.push_back(nums[i]);
}
vec.resize(k);
return vec;
}
vector<int> merge(vector<int> nums1, vector<int> nums2) {
vector<int> vec;
while(nums1.size() + nums2.size() > 0) {
// We compare nums1 and nums2, because we do not only care about the current position, but also the next position
vector<int>& tem = nums1 > nums2 ? nums1 : nums2; // vector<int> has the exact compare function that we need
vec.push_back(tem[0]);
tem.erase(tem.begin());
}
return vec;
}
};