leetcode
leetcode 2651 ~ 2700
拼接最大数

拼接最大数

难度:

标签:

题目描述

You are given two integer arrays nums1 and nums2 of lengths m and n respectively. nums1 and nums2 represent the digits of two numbers. You are also given an integer k.

Create the maximum number of length k <= m + n from digits of the two numbers. The relative order of the digits from the same array must be preserved.

Return an array of the k digits representing the answer.

 

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]

 

Constraints:

  • m == nums1.length
  • n == nums2.length
  • 1 <= m, n <= 500
  • 0 <= nums1[i], nums2[i] <= 9
  • 1 <= k <= m + n

代码结果

运行时间: 72 ms, 内存: 16.8 MB


/*
 * Problem: Given two integer arrays nums1 and nums2, representing the digits of two numbers, 
 * and an integer k, find the maximum number of length k that can be formed from digits of nums1 and nums2, 
 * while maintaining the relative order of digits from each array.
 *
 * Approach: 
 * 1. Create a helper function to find the maximum subsequence of a given length from a single array using Streams.
 * 2. Iterate over all possible lengths for subsequences from both arrays such that their combined length is k.
 * 3. Merge the two subsequences to form the largest possible number.
 * 4. Compare the merged number with the current maximum and update if necessary.
 */

import java.util.Arrays;
import java.util.stream.IntStream;

public class SolutionStream {
    public int[] maxNumber(int[] nums1, int[] nums2, int k) {
        int m = nums1.length, n = nums2.length;
        int[] maxSubsequence = new int[k];
        for (int i = Math.max(0, k - n); i <= Math.min(k, m); i++) {
            int[] subsequence1 = maxSubsequence(nums1, i);
            int[] subsequence2 = maxSubsequence(nums2, k - i);
            int[] candidate = merge(subsequence1, subsequence2);
            if (greater(candidate, 0, maxSubsequence, 0)) {
                maxSubsequence = candidate;
            }
        }
        return maxSubsequence;
    }

    private int[] maxSubsequence(int[] nums, int k) {
        int[] subsequence = new int[k];
        int top = -1;
        int remain = nums.length - k;
        for (int num : nums) {
            while (top >= 0 && subsequence[top] < num && remain > 0) {
                top--;
                remain--;
            }
            if (top < k - 1) {
                subsequence[++top] = num;
            } else {
                remain--;
            }
        }
        return subsequence;
    }

    private int[] merge(int[] nums1, int[] nums2) {
        return IntStream.concat(IntStream.of(nums1), IntStream.of(nums2))
                        .boxed()
                        .sorted((a, b) -> b - a)
                        .mapToInt(i -> i)
                        .toArray();
    }

    private boolean greater(int[] nums1, int i, int[] nums2, int j) {
        while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
            i++;
            j++;
        }
        return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);
    }
}

解释

方法:

题解思路分为几个步骤: 1. 对于 nums1 和 nums2 分别生成所有可能的子序列(通过删除元素)的非降序排列。 2. 然后对于每个可能的 i(i 代表从 nums1 中选择的元素数量,k-i 代表从 nums2 中选择的元素数量),将 nums1 中的前 i 个元素和 nums2 中的前 k-i 个元素合并成最大的数。 3. 比较所有合并后的数,找到最大的那个。

时间复杂度:

O(m^2 + n^2 + k*min(m,k))

空间复杂度:

O(m^2 + n^2 + k)

代码细节讲解

🦆
为什么在生成最大子序列时选择使用单调栈,并且如何确保通过单调栈得到的序列是最大的?
单调栈是一种特殊的栈结构,用于在遍历数组时维护一个单调递增或单调递减的序列。在拼接最大数的问题中,我们使用单调递减栈。这是因为我们希望尽可能地让高位的数字大,以此来构造一个最大的数。单调栈可以帮助我们处理当前数字和栈顶数字的关系,如果当前数字比栈顶数字大,则将栈顶数字弹出,这样可以确保在不违反选取数字个数限制的前提下,尽可能地让后续的大数字处于高位,从而构成更大的数。单调栈确保了每次插入操作后栈内元素的单调性,这样生成的序列自然是最大的非降序排列。
🦆
在合并两个子序列以形成最大数时,如何处理两个序列的前缀相同时的判断逻辑?具体的,当`s1[i:] == s2[j:]`时,应该如何选择下一个数字?
在合并两个子序列时,如果遇到`s1[i:] == s2[j:]`的情况,即从当前位置开始两个子序列的剩余部分完全相等,我们可以选择任意一个序列中的下一个数字,因为它们会给出相同的结果。然而,为了避免在实现中出现不必要的复杂性,通常的做法是按照一定的顺序(例如先取`s1`或先取`s2`)来推进,这可以简化代码逻辑。实际操作中,我们可以基于其他条件(如先到达序列末尾的情况)来调整选择策略,以确保合并后的序列尽可能大。
🦆
在实际代码实现中,如何保持从同一数组中取出的数字的相对顺序不变?
在从数组中构造子序列时,我们需要确保取出的数字保持其在原数组中的相对顺序。这可以通过遍历数组的同时使用单调栈来实现。单调栈操作只涉及栈顶元素的比较和可能的弹出,这不会影响未处理的元素的顺序。因此,已经进入栈中的元素(即已经被选择加入到子序列中的元素)的相对顺序是保持不变的。这样,即使在进行删除操作(弹栈操作)时,也只是选择不将某些元素加入子序列,而不会改变已加入元素的顺序。

相关问题

移掉 K 位数字

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

 

示例 1 :

输入:num = "1432219", k = 3
输出:"1219"
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。

示例 2 :

输入:num = "10200", k = 1
输出:"200"
解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 :

输入:num = "10", k = 2
输出:"0"
解释:从原数字移除所有的数字,剩余为空就是 0 。

 

提示:

  • 1 <= k <= num.length <= 105
  • num 仅由若干位数字(0 - 9)组成
  • 除了 0 本身之外,num 不含任何前导零

最大交换

给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。

示例 1 :

输入: 2736
输出: 7236
解释: 交换数字2和数字7。

示例 2 :

输入: 9973
输出: 9973
解释: 不需要交换。

注意:

  1. 给定数字的范围是 [0, 108]