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.
Note: You should try to optimize your time and space complexity.
Example 1:
Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[9, 8, 6, 5, 3]
Example 2:
Input:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
Output:
[6, 7, 6, 0, 4]
Example 3:
Input:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
Output:
[9, 8, 9]
思路
这题运用的是一个动态规划的解题思路:把目标长度为k的串分成两个部分,长度为p的部分来自于第一个字符串,长度为(k-p)的部分来自于第二个字符串,这些组合中取得的最大的串就是答案。
f
(
x
,
y
)
=
max
0
≤
p
≤
k
{
m
e
r
g
e
(
m
a
x
A
r
r
a
y
(
n
u
m
s
1
,
p
)
,
m
a
x
A
r
r
a
y
(
n
u
m
s
2
,
k
−
p
)
)
}
f(x,y) = \max_{0\le p\le k}\{merge(maxArray(nums1,p),maxArray(nums2,k-p))\}
f(x,y)=0≤p≤kmax{merge(maxArray(nums1,p),maxArray(nums2,k−p))}
其中merge是把两个串连接起来,不改变相对顺序时能得到的最大的串。maxArray是从一个串中提取若干个字符,能取得的最大值的串。
实现
这一题动态规划的推导式是比较简单的,但merge和maxArray函数需要花一点工夫。
merge
这个merge运用到了一点贪心算法的思想,在两个串中,在当前条件下,取出来剩下串比较大的一个,把其首元素放到新队列里,直到两个串中都没有剩余元素。
这里有一个问题是,为什么要把剩下的两个串整个进行比较而不是只比较首元素的大小呢?考虑下面的例子:merge([6,7], [6,0,4]),合并后最大的串是[6,7,6,0,4],如果仅仅是比较首元素的大小,相等无法确定,若先取后者的6,那么结果为[6,6,7,0,4],显然没有前面的一个大。
maxArray
maxArray的作用是在一个指定数组中,取出指定多的项(设为len),这些项组成的串最大。这里我们的想法很简单:设置一个空串ans,当原串里还有足够的字符,而且当前的输入比ans里的最后一个元素大的话,弹出ans的最后一个元素,当无法弹出后,把剩余串的首字符放进去。
为了能看到为什么这个算法能正常工作,我们留意到,每次插入新元素后,下面的关系总成立:在原串中截止到当前元素为止,ans存放的就是当前最大的可能的串,而且ans中的串与原串中剩下字符连接起来的长度不会小于k。
代码
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
vector<int> answer;
for(int p = 0; p <= k; ++p) {
if(p > nums1.size() || k - p > nums2.size()) {
continue;
}
vector<int> pieces1 = maxArray(nums1, p);
vector<int> pieces2 = maxArray(nums2, k - p);
vector<int> newAns = merge(pieces1, pieces2);
if(answer.size() == 0 || greater(newAns, 0, answer, 0)) {
answer = newAns;
}
}
return answer;
}
vector<int> merge(vector<int>& a, vector<int>& b) {
vector<int> ans;
for(int i = 0, j = 0; i != a.size() || j != b.size(); ) {
ans.push_back(greater(a, i, b, j) ? a[i++] : b[j++]);
}
return ans;
}
vector<int> maxArray(vector<int>& vec, int len) {
if(vec.size() <= len) {
return vec;
}
vector<int> ans(len, INT_MIN);
if(len == 0) {
return ans;
}
int n = vec.size();
for(int i = 0, j = 0; i != n; ++i) {
while(j > 0 && n - i - 1 + j >= len && vec[i] > ans[j-1]) {
--j;
}
if(j != len) {
ans[j++] = vec[i];
}
}
return ans;
}
bool greater(vector<int> a, int abeg, vector<int> b, int bbeg) {
while(abeg != a.size() && bbeg != b.size() && a[abeg] == b[bbeg]) {
++abeg;
++bbeg;
}
return bbeg == b.size() || (abeg != a.size() && a[abeg] > b[bbeg]);
}
};