321. Create Maximum Number
Hard
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]
题意
给定两个数组nums1和nums2以及一个正整数k,在nums1和nums2中共取k个数字,使得这k个数字顺序排成的正整数数值最大
思路
遍历k在两个数组中所有可能的切分,对于每一个切分,在nums1, nums2中分别找到可以组成最大正整数的子序列,并按照可以组成最大的正整数的原则将nums1, nums2的两个子序列合并,取所有切分中合并得到的最大序列
难点在于合并方法mergeMax的书写。不同于mergeSort里最小合并,最大合并当nums1[i] == nums2[j]时要比较后缀nums1[i:]与后缀nums2[j:]的大小,取后缀大者对应的第i或第j个元素放入merge后的数组。
代码
class Solution {
/**
* pick max integer with length {@code k} from {@code nums}
* ensure {@code k} <= {@code nums}.length
* @time complexity: O(n)
*/
private int[] pickMax(int[] nums, int k) {
int n = nums.length;
if (n == k) {
return nums;
}
int[] ret = new int[k];
int left = 0, right = 0, i = 0, cur = left;
for (i=0; i<k; ++i) {
right = n - k + i;
for (cur = left; cur <= right; ++cur) {
if (nums[cur] > ret[i]) {
ret[i] = nums[cur];
left = cur + 1;
}
}
}
return ret;
}
/**
* merge {@code nums1} and {@code nums2} for max integer
* @time complexity: O(n1 + n2)
*/
private int[] mergeMax(int[] nums1, int[] nums2) {
int n1 = nums1.length, n2 = nums2.length, i = 0, j = 0, k = 0;
if (n1 == 0) {
return nums2;
}
if (n2 == 0) {
return nums1;
}
int[] merged = new int[n1 + n2];
while (i < n1 && j < n2) {
if (nums1[i] > nums2[j]) {
merged[k++] = nums1[i++];
} else if (nums2[j] > nums1[i]) {
merged[k++] = nums2[j++];
} else {
if (i + 1 == n1 && j + 1 == n2) {
merged[k++] = nums1[i++];
} else if (i + 1 == n1) {
merged[k++] = nums2[j++];
} else if (j + 1 == n2) {
merged[k++] = nums1[i++];
} else {
int ii = i, jj = j;
while (++ii < n1 && ++jj < n2 && nums1[ii] == nums2[jj]);
if (ii == n1 && jj == n2) {
merged[k++] = nums1[i++];
} else if (ii == n1) {
merged[k++] = nums2[j++];
} else if (jj == n2) {
merged[k++] = nums1[i++];
} else if (nums1[ii] > nums2[jj]) {
merged[k++] = nums1[i++];
} else {
merged[k++] = nums2[j++];
}
}
}
}
while (i < n1) {
merged[k++] = nums1[i++];
}
while (j < n2) {
merged[k++] = nums2[j++];
}
return merged;
}
/**
* compare two integers represented by two arrays with same length
* @param nums1
* @param nums2
* @return -1: nums1 < nums2; 0: nums1 = nums2; 1: nums1 > nums2
* @time complexity: O(n)
*/
private int compareArray(int[] nums1, int[] nums2) {
int n = nums1.length, i = 0;
for (i=0; i<n; ++i) {
if (nums1[i] < nums2[i]) {
return -1;
} else if (nums1[i] > nums2[i]) {
return 1;
}
}
return 0;
}
/**
* @time complexity: O(k(n1+n2))
*/
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int n1 = nums1.length, n2 = nums2.length, i = 0;
int[] ans = new int[k];
for (i=Math.max(0, k-n2); i<=Math.min(n1, k); ++i) {
int[] p1 = pickMax(nums1, i);
int[] p2 = pickMax(nums2, k - i);
int[] merged = mergeMax(p1, p2);
// printArray(p1);
// printArray(p2);
// printArray(merged);
// System.out.println();
if (compareArray(merged, ans) == 1) {
ans = merged;
}
}
return ans;
}
/**
* for debug
*/
private void printArray(int[] nums) {
for (int num: nums) {
System.out.print(num);
}
System.out.println();
}
}