在问 · 算法题解

321. 拼接最大数

题目描述

输入两个整数数组 nums1 和 nums2,长度分别为 m 和 n,元素都是 0 到 9 的数字,分别表示两个数从高到低的各位数字。另外输入一个整数 k,满足 k <= m + n。

从两个数组中一共选出 k 个数字,拼成一个 k 位的数。来自同一个数组的数字必须保持它们在原数组里的先后顺序,两个数组的数字之间可以任意穿插。

让拼出来的数尽可能大,返回它的各位数字组成的长度为 k 的数组。

示例 1
输入:nums1 = [3,4,6,5], nums2 = [9,1,2,5,8,3], k = 5
输出:[9,8,6,5,3]

解释:从 nums1 取 6,5,从 nums2 取 9,8,3,合并成 [9,8,6,5,3]。

示例 2
输入:nums1 = [6,7], nums2 = [6,0,4], k = 5
输出:[6,7,6,0,4]

解释:两个数组的数字全都要用上,交错排列后最大的是 [6,7,6,0,4]。

示例 3
输入:nums1 = [3,9], nums2 = [8,9], k = 3
输出:[9,8,9]

解释:从 nums2 取 8,9,从 nums1 取 9,最大是 [9,8,9]。

数据范围

  • m == nums1.length
  • n == nums2.length
  • 1 <= m, n <= 500
  • 0 <= nums1[i], nums2[i] <= 9
  • 1 <= k <= m + n
  • nums1 和 nums2 都没有前导 0。

解题思路

把问题拆成三层。第一层:枚举从 nums1 取 i 个、从 nums2 取 k-i 个。第二层:在单个数组里取固定 t 个数字使其最大,这是单调栈问题,可以删掉 len-t 个数字:遇到更大的数字时,只要还有删除额度,就弹出比它小的栈顶。第三层:把两个子序列合并成最大的序列,每一步比较两边“剩余的整个后缀”的字典序,取较大一边的首位(只比首位在相等时会出错)。所有拆分的结果中取字典序最大的一个。

为什么正确

pick 过程中,栈加上剩余元素始终至少有 t 个(drop 只会在仍有富余时减少),并且在可用的删除额度内,栈是单调不增的;merge 时每次取的都是“后缀字典序更大”的那一边的首位,所以合并结果保留了两边能提供的最大前缀。

复杂度分析

时间 O(k·(m+n)²):共 O(k) 种拆分,每种 pick 是 O(m+n),merge 每步比较后缀最坏 O(m+n),共 O((m+n)²);空间 O(m+n)。

易错点

合并时不能只比较两个首位:首位相等时要继续比较后面,否则比如 [6,7] 与 [6,0,4] 会先取错 6,得到 [6,6,7,0,4] 而不是 [6,7,6,0,4]。另外枚举 i 的范围是 max(0, k-n) 到 min(k, m),越界会让 pick 拿到负数个数字。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def maxNumber(self, nums1: List[int], nums2: List[int], k: int) -> List[int]:
        def pick(nums, t):
            drop = len(nums) - t
            stack = []
            for x in nums:
                while drop and stack and stack[-1] < x:
                    stack.pop()
                    drop -= 1
                stack.append(x)
            return stack[:t]

        def merge(a, b):
            res = []
            while a or b:
                if a > b:
                    res.append(a.pop(0))
                else:
                    res.append(b.pop(0))
            return res

        best = []
        for i in range(max(0, k - len(nums2)), min(k, len(nums1)) + 1):
            cand = merge(pick(nums1, i), pick(nums2, k - i))
            if cand > best:
                best = cand
        return best

载入编辑器,自己跑一遍 →

栈与堆 专题的其他题目

查看 栈与堆 全部题目 →