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]
思想:从nums1中选择i个数,从nums2中选择k-i个数,使得合成的数最大。
分成两个子问题:
1. 如何从一个数组中选择i个数,使这i个数表示的数值是所有候选中最大的。
比如[9, 1, 2, 5, 8, 3] i = 2;如何得到 [9,8]。
2. 如何合并两个数组使得形成最大的值
比如[9, 8, 3] [6,5] ;如何得到[9,8,6,5,3]
To solve this problem, first let’s look at simpler version:
Given one array of length n
, create the maximum number of length k
.
The solution to this problem is Greedy with the help of stack. The recipe is as following
nums
nums[i]
until
k
k
push nums[i]
Since the stack length is known to be k
, it is very easy to use an array to simulate the stack.
The time complexity is O(n)
since each element is at most been pushed and popped once.
Java
Given two array of length m
and n
, create maximum number of length k = m + n
.
OK, this version is a lot closer to our original problem with the exception that we will use all the digits we have.
Still, for this version, Greedy is the first thing come to mind. We have k
decisions to make, each time will just need to decide ans[i]
is from which of the two. It seems obvious, we should always choose the larger one right? This is correct, but the problem is what should we do if they are equal?
This is not so obvious. The correct answer is we need to see what behind the two to decide. For example,
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
ans = [6, 7, 6, 0, 4]
We decide to choose the 6
from nums1
at step 1, because 7 > 0
. What if they are equal again? We continue to look the next digit until they are not equal. If all digits are equal then choose any one is ok. The procedure is like the merge in a merge sort. However due to the “look next until not equal”, the time complexity is O(nm).
As @lixx2100 mentioned that it is possible to have a linear time merge algorithm based on suffix array. See here andhere. But there isn’t a short implementation for suffix array construction in linear time.
Java
Now let’s go back to the real problem. First, we divide the k
digits required into two parts, i
and k-i
. We then find the maximum number of length i
in one array and the maximum number of length k-i
in the other array using the algorithm in section 1. Now we combine the two results in to one array using the algorithm in section 2. After that we compare the result with the result we have and keep the larger one as final answer.
Java
1
2
3
4
5
6
7
8
9
10
|
public
int
[
]
maxNumber
(
int
[
]
nums1
,
int
[
]
nums2
,
int
k
)
{
int
n
=
nums1
.
length
;
int
m
=
nums2
.
length
;
int
[
]
ans
=
new
int
[
k
]
;
for
(
int
i
=
Math
.
max
(
0
,
k
-
m
)
;
i
<=
k
&&
i
<=
n
;
++
i
)
{
int
[
]
candidate
=
merge
(
maxArray
(
nums1
,
i
)
,
maxArray
(
nums2
,
k
-
i
)
,
k
)
;
if
(
greater
(
candidate
,
0
,
ans
,
0
)
)
ans
=
candidate
;
}
return
ans
;
}
|
class Solution { public: vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) { int m = nums1.size(); int n = nums2.size(); vector<int> res(k,0); for(int i=max(0,k-n); i<=min(m,k); i++){ auto res1 = findMaxKValue(nums1,i); auto res2 = findMaxKValue(nums2,k-i); auto temp = merge(res1,res2); if(compare(temp,0,res,0)) res = temp; } return res; } vector<int> findMaxKValue(vector<int>& nums, int k){ vector<int> res; if(k==0) return res; res = vector<int>(k,0); int j=0; int n = nums.size(); for(int i=0; i<n; i++){ while(j>0 && n-i+j>k && nums[i]>res[j-1]) j--; if(j<k) res[j++] = nums[i]; } return res; } vector<int> merge(vector<int>& nums1, vector<int>& nums2){ int m = nums1.size(); int n = nums2.size(); if(!m) return nums2; if(!n) return nums1; vector<int> res(m+n,0); int i=0, j=0, k=0; while(i<m || j<n){ res[k++] = compare(nums1,i,nums2,j)? nums1[i++]:nums2[j++]; } return res; } bool compare(vector<int>& res1,int i, vector<int>& res2,int j){ while(i<res1.size() && j<res2.size() && res1[i]==res2[j]){ i++; j++; } return j==res2.size()|| (i<res1.size() && res1[i]>res2[j]); } };