【困难】力扣算法题解析LeetCode321:拼接最大数

关注文末推广名片,即可免费获得本题测试源码

题目来源:LeetCode321:拼接最大数

问题抽象: 给定两个整数数组 nums1nums2(元素为 0-9 的整数)及一个整数 k,要求从两个数组中分别按原始顺序抽取子序列(保持元素相对位置),并拼接成一个长度为 k 的最大整数序列(字典序最大),满足以下核心需求:

  1. 拼接规则定义

    • 独立抽取:从 nums1 抽取长度 x 的子序列,从 nums2 抽取长度 k-x 的子序列(0 ≤ x ≤ min(len(nums1), k)k-x ≤ len(nums2));
    • 顺序拼接:将两个子序列按原始顺序合并(可交替插入,但需保持各自子序列内顺序);
    • 最大化字典序:最终序列的字典序需为所有可能拼接结果中的最大值(如 [9,8] > [9,7])。
  2. 输入与输出

    • 输入:
      • 数组 nums1, nums2(长度 0 ≤ m, n ≤ 500);
      • 整数 k1 ≤ k ≤ m+n);
    • 输出:字典序最大的拼接序列(整数数组)。
  3. 计算约束

    • 时间复杂度 O(k^3)(三重循环:枚举长度组合+子序列生成+合并比较);
    • 空间复杂度 O(k)(存储候选序列);
    • 需处理 最大规模输入m=n=500, k=1000)。
  4. 边界处理

    • 空数组:若 nums1 为空,则只能从 nums2k 个元素(反之亦然);
    • 全零数组:如 nums1=[0,0], nums2=[0], k=3 → 输出 [0,0,0]
    • 大数优先:如 nums1=[6,7], nums2=[6,0,4], k=5 → 最优解 [6,7,6,0,4]
    • 等值竞争:如 nums1=[4,6], nums2=[4,5],合并时需比较后续元素(取 [4,6,4,5] 而非 [4,4,6,5])。

输入:整数数组 nums1, nums2,整数 k
输出:字典序最大的拼接序列(整数数组)。


解题思路

本题要求从两个数组 nums1nums2 中保持数字相对顺序不变,选取数字组成长度为 k 的最大数。核心思路是枚举从 nums1 中选取 i 个数字、从 nums2 中选取 k-i 个数字的所有可能组合(i 的范围需满足 k-n <= i <= min(k, m)),然后对每种组合:

  1. 分别从 nums1nums2 中提取长度为 ik-i 的最大子序列(使用单调栈算法)。
  2. 合并两个子序列,合并时采用贪心策略,每次选取当前字典序更大的子序列的头部元素。
  3. 比较所有合并后的序列,保留字典序最大的序列作为结果。

代码实现(Java版)🔥点击下载源码

class Solution {
    public int[] maxNumber(int[] nums1, int[] nums2, int k) {
        int m = nums1.length, n = nums2.length;
        int[] res = new int[k];
        Arrays.fill(res, -1); // 初始化结果数组
        
        int start = Math.max(0, k - n); // i的最小值
        int end = Math.min(k, m);       // i的最大值
        
        for (int i = start; i <= end; i++) {
            // 从nums1中提取长度为i的最大子序列
            int[] sub1 = maxSubsequence(nums1, i);
            // 从nums2中提取长度为k-i的最大子序列
            int[] sub2 = maxSubsequence(nums2, k - i);
            // 合并两个子序列
            int[] candidate = merge(sub1, sub2);
            
            // 更新结果:若候选序列字典序大于当前结果,则替换
            if (compare(candidate, 0, res, 0) > 0) {
                res = candidate;
            }
        }
        return res;
    }
    
    // 从一个数组中提取长度为k的最大子序列(单调栈实现)
    private int[] maxSubsequence(int[] nums, int k) {
        if (k == 0) return new int[0];
        int n = nums.length;
        int[] stack = new int[k]; // 栈
        int top = -1;             // 栈顶指针
        
        for (int i = 0; i < n; i++) {
            // 当栈非空、当前元素大于栈顶元素,且剩余元素足够填满栈时,弹出栈顶
            while (top >= 0 && nums[i] > stack[top] && top + n - i >= k) {
                top--;
            }
            // 栈未满时压入当前元素
            if (top < k - 1) {
                stack[++top] = nums[i];
            }
        }
        return stack;
    }
    
    // 合并两个子序列,生成字典序最大的序列
    private int[] merge(int[] nums1, int[] nums2) {
        int m = nums1.length, n = nums2.length;
        if (m == 0) return nums2;
        if (n == 0) return nums1;
        
        int[] res = new int[m + n];
        int i = 0, j = 0, idx = 0;
        // 双指针遍历,每次选取字典序更大的序列的头部元素
        while (i < m && j < n) {
            if (compare(nums1, i, nums2, j) > 0) {
                res[idx++] = nums1[i++];
            } else {
                res[idx++] = nums2[j++];
            }
        }
        // 处理剩余元素
        while (i < m) res[idx++] = nums1[i++];
        while (j < n) res[idx++] = nums2[j++];
        return res;
    }
    
    // 比较两个数组从指定位置开始的字典序
    private int compare(int[] nums1, int p1, int[] nums2, int p2) {
        int len1 = nums1.length - p1;
        int len2 = nums2.length - p2;
        int minLen = Math.min(len1, len2);
        // 逐位比较
        for (int i = 0; i < minLen; i++) {
            if (nums1[p1 + i] != nums2[p2 + i]) {
                return nums1[p1 + i] - nums2[p2 + i];
            }
        }
        // 前缀相同,长度更长的序列字典序更大
        return len1 - len2;
    }
}

代码说明

  1. 主函数 maxNumber

    • 初始化结果数组 res-1(确保第一次比较时候选序列能覆盖它)。
    • 计算 i 的合理范围:start = max(0, k-n)end = min(k, m)
    • 遍历所有可能的 i,生成候选序列并更新最大结果。
  2. maxSubsequence 函数

    • 使用单调栈提取长度为 k 的最大子序列。
    • 遍历数组时,若当前元素大于栈顶元素且剩余元素足够填满栈,则弹出栈顶。
    • 栈未满时压入当前元素。
  3. merge 函数

    • 双指针遍历两个子序列,每次选取字典序更大的序列的头部元素。
    • 使用 compare 函数比较两个序列的字典序。
  4. compare 函数

    • 比较两个数组从指定位置开始的字典序。
    • 逐位比较元素,遇到不同元素返回差值。
    • 前缀相同时,长度更长的序列字典序更大。

提交详情(执行用时、内存消耗)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值