- Create Maximum Number
https://leetcode.com/problems/create-maximum-number/description/
Difficulty:Hard
Total Accepted:27.3K
Total Submissions:109.3K
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 的最大拼接字符串。
总的思路如下:
- 分别找出两个子字符串的最大子序列,在nums1(长度为m)和nums2(长度为n)中挑选出i(max(0, k - n) <= i <= min(m, k) 和k-i个数
- 将两个子序列拼接在一起。
有了上面的总思路,整个流程也就相对清晰了。主要的就是两个函数的实现,一个是找出最大子序列,一个是在不改变相对位置的情况下拼接。
先说找最大子序列这个函数的实现,基本思路如下:
- 因为子序列个数必然小于该字符串,假设要从vec1中选出i个数字,首先创造出一个i长度的vector,先将vec1的前i个加进去。接下来,对于后面的每一个想要加进来的数字,首先将vector里面的第一个小于后面的数字踢掉,其余的前进一位,然后把新的加到最后面。如果没有小于后面的数,则将新加进来的数和vector最后一个比较,如果更大的话则替代掉最后一个。
- 例子如下:
9 5 8 3
加入4
踢掉5,4加到尾部
9 8 3 4
加入2
踢掉3,2加到尾部
9 8 4 2
下面是合并函数的基本思路:
- 先考虑下面两个情况:
合并:8 9 和3 4 2
合并:6 7 和 6 0 4
- 对于第一个例子,显然比较好判断,逐个比较两个数的头部,哪个更大先合并哪一个。
- 对于第二个例子,显然没那么好判断。毕竟两个数字头部都是6。当然根据分析知道要先将6 7放进去,因为第一个6后面跟着7,而第二个6后面只是0。经过分析,解决方法是:如果遇到相同的数字,比较跟在他们后面的序列大小。
- 例如,上面的第二个例子,跟着的两个序列一个是7,另一个是04,显然7比04要大。具体实现比较两个不同长度的序列大小,方法可以是转为数字或者加0补齐等等。
综上,总的程序实现如下:
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
vector<int> result(k, 0);
int m = nums1.size(), n = nums2.size();
for (int i = max(0, k - n); i <= min(m, k); i++) {
vector<int> sub1 = findMaxSubArr(nums1, i);
vector<int> sub2 = findMaxSubArr(nums2, k - i);
vector<int> mergeTemp = mergeSubArr(sub1, sub2);
if (compareVec(result, 0, mergeTemp, 0))result = mergeTemp;
}
return result;
}
bool compareVec(vector<int> vec1, int num1, vector<int> vec2, int num2) {//后面更大,返回false
//先补齐长度
if (num1 > vec1.size())return false;
if (num2 > vec2.size())return true;
int length1 = vec1.size() - num1;
int length2 = vec2.size() - num2;
vector<int> res1(max(length1, length2), 0);
vector<int> res2(max(length1, length2), 0);
if (length1 >= length2) {//补vec2
for (int i = 0; i < length1; i++) {
res1[i] = vec1[i + num1];
}
for (int i = 0; i < length2; i++) {
res2[i + length1 - length2] = vec2[i + num2];
}
}
else {
for (int i = 0; i < length2; i++) {
res2[i] = vec2[i + num2];
}
for (int i = 0; i < length1; i++) {
res1[i + length2 - length1] = vec1[i + num1];
}
}
int i = 0;
while (i < res1.size() && res1[i] == res2[i]) {
i++;
}
return res1[i] < res2[i];
}
//分别找出两个子字符串的最大子序列,长度为nums1(长度为m)和nums2(长度为n)中挑选出i(max(0, k - n) <= i <= min(m, k) 和k-i个数
vector<int> findMaxSubArr(vector<int> vec, int i) {
vector<int> subArr(i, 0);
if (i == 0)return subArr;
//将前i个先写入
for (int j = 0; j < i; j++) {
subArr[j] = vec[j];
}
//每塞入一个把subArr中第一个小于后面的数给踢出
for (int j = i; j < vec.size(); j++) {
int pos = -1;
for (int x = 0; x < i - 1; x++) {
if (subArr[x] < subArr[x + 1]) {
pos = x;
break;
}
}
//没找到,就和最后一个比较
if (pos == -1 && subArr[i - 1] < vec[j]) {
subArr[i - 1] = vec[j];
}
else if (pos != -1) {
for (int x = pos; x < i - 1; x++) {
subArr[x] = subArr[x + 1];
}
subArr[i - 1] = vec[j];
}
}
return subArr;
}
//合并两个最大子序列
vector<int> mergeSubArr(vector<int>& nums1, vector<int>& nums2) {
int num1Length = nums1.size(), num2Length = nums2.size();
int i = 0, j = 0;
vector<int> mergeArr(num1Length + num2Length, 0);
if (!num1Length)return nums2;
if (!num2Length)return nums1;
while (i +j <num1Length+ num2Length) {
if (j==num2Length||
i < num1Length &&
(nums1[i] > nums2[j] ||
(nums1[i] == nums2[j] && !compareVec(nums1, i + 1, nums2, j + 1)))) {
mergeArr[i+j] = nums1[i];
i++;
}
else {
mergeArr[i+j] = nums2[j];
j++;
}
}
return mergeArr;
}
};