Question

Formatted question description: https://leetcode.ca/all/321.html

 321	Create Maximum Number

 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]

 @tag-dp

Algorithm

This question gave us two arrays, where the numbers are unordered, and gave us a k value of k <= n1 + n2, and then picked out k numbers from the two arrays, the relative order between the numbers is not Change, find the largest number that can be composed. The difficulty of this question is Hard, and the blogger has never come up with a solution, so he can refer to the solution of the great online gods. Since the size of k is uncertain, there are three possibilities:

The first is when k is 0, no number is taken in the two arrays.

The second is when k is not greater than the length of any one of the arrays. In this case, it is possible to take numbers from only one array, or take some of both.

The third case is that k is greater than the length of any one of the arrays, and you need to take the numbers from the two arrays separately. As for how many from each array, you must consider each case, and then change the result each time.

In order to be able to handle these three situations at the same time, assuming that the number of i is taken from the array nums1, then the number of k-i needs to be taken from nums2. So how to determine the range of i? From the second case, if k is not greater than the length of any array, then it is possible to take only k from one of the arrays, that is to say, it is not necessary to take the number from nums1, so i can be as small as 0. If it is the third case, assuming that k is greater than the length of nums2, even if all the numbers in nums2 are taken out, there is no way to make up k. The extra k-n2 numbers must be taken from nums1. So just compare the size of 0 and k-n2, and take the larger one as the starting range of i. So how big i can be depends on the size of k and n1. If k is less than or equal to n1, then only k is required. If k is greater than n1, then only n1 can be selected from nums1. Take it from nums2.

Ok, now I know the situation of taking numbers from two arrays respectively. Here, I hope that the i numbers taken from nums1 are the largest combination. Similarly, the k-i numbers taken from nums2 are also the largest combination of numbers. How can we extract the largest combination? For example, the length of the current array is n, k numbers need to be taken out, and a variable drop = n-k is defined to indicate the number of numbers that need to be dropped. The numbers in the array are traversed and the following loop is performed. If drop is an integer at this time, and the result The length of the array is not 0, and the last element of the result array is less than the element currently traversed, then the last element of the result array is removed. At this time, drop is decremented by 1, and the loop is repeated until any of the above conditions are not met, and then the current element is added to the result array , And finally return the first k elements in the result array.

Now I have taken out the i largest combination numbers from nums1 and ki largest combination numbers from nums2. The last step is to mix and sort the two extracted arrays into an array, between the respective numbers in the small arrays The relative order remains unchanged. For a mixture of two arrays, compare the sizes of the two arrays (by element comparison), then take the first element from the current larger array, and then delete the head element until the next time and then compare again, until the two arrays are both Stop for nothing.

Code

Java

import java.util.Arrays;

public class Create_Maximum_Number {

    public static void main(String[] args) {
        Create_Maximum_Number out = new Create_Maximum_Number();
        Solution s = out.new Solution();

        System.out.println(Arrays.toString(s.maxNumber(new int[]{3, 4, 6, 5}, new int[]{9, 1, 2, 5, 8, 3}, 5)));
    }

    // First find out the maximum number for each array,
    // and then merge it into a global maximal one.
    public class Solution {
        public int[] maxNumber(int[] nums1, int[] nums2, int k) {
            int[] result = new int[k];
            int n1 = nums1.length;
            int n2 = nums2.length;

            // similar idea to 3-d DP: start from 0 to k
            for (int i = Math.max(0, k - n2); i <= Math.min(n1, k); i++) {

                // step 1: find the largest number from each array, and merge into one
                int[] list1 = findMax(nums1, i);
                int[] list2 = findMax(nums2, k - i);

                // step 2: then merge into one
                int[] curr = merge(list1, list2); // possible overflow

                if (greater(curr, 0, result, 0)) {
                    result = curr;
                }
            }

            return result;
        }

        private int[] findMax(int[] nums, int k) {
            int[] result = new int[k];

            int n = nums.length;
            int resultPointer = 0;

            // {9, 1, 2, 5, 8, 3}, k=5
            for (int i = 0; i < n; i++) {
                // @note: current digits larger than k needed digits
                //          len(current count in result) + n-1 (left in original array)

                int dropNeeded = n - k - (i - resultPointer);
                while (resultPointer > 0 && dropNeeded > 0 && nums[i] > result[resultPointer - 1]) {
                    resultPointer--;
                }

                if (resultPointer < k) {
                    result[resultPointer] = nums[i];
                    resultPointer++;
                }
            }

            return result;
        }

        private int[] merge(int[] list1, int[] list2) {
            int n1 = list1.length;
            int n2 = list2.length;

            int[] result = new int[n1 + n2];

            int i = 0;
            int j = 0;
            int k = 0;

            while (k < n1 + n2) {
                if (greater(list1, i, list2, j)) {
                    result[k++] = list1[i++];
                } else {
                    result[k++] = list2[j++];
                }
            }

            return result;
        }

        private boolean greater(int[] list1, int startPos1, int[] list2, int startPos2) {
            int n1 = list1.length;
            int n2 = list2.length;

            while (startPos1 < n1 && startPos2 < n2 && list1[startPos1] == list2[startPos2]) {
                startPos1++;
                startPos2++;
            }

            if (startPos2 == n2) {
                return true;
            }

            if (startPos1 < n1 && list1[startPos1] > list2[startPos2]) {
                return true;
            }

            return false;
        }
    }
}

All Problems

All Solutions