题目来源:LeetCode321:拼接最大数
问题抽象: 给定两个整数数组 nums1 和 nums2(元素为 0-9 的整数)及一个整数 k,要求从两个数组中分别按原始顺序抽取子序列(保持元素相对位置),并拼接成一个长度为 k 的最大整数序列(字典序最大),满足以下核心需求:
-
拼接规则定义:
- 独立抽取:从
nums1抽取长度x的子序列,从nums2抽取长度k-x的子序列(0 ≤ x ≤ min(len(nums1), k)且k-x ≤ len(nums2)); - 顺序拼接:将两个子序列按原始顺序合并(可交替插入,但需保持各自子序列内顺序);
- 最大化字典序:最终序列的字典序需为所有可能拼接结果中的最大值(如
[9,8] > [9,7])。
- 独立抽取:从
-
输入与输出:
- 输入:
- 数组
nums1,nums2(长度0 ≤ m, n ≤ 500); - 整数
k(1 ≤ k ≤ m+n);
- 数组
- 输出:字典序最大的拼接序列(整数数组)。
- 输入:
-
计算约束:
- 时间复杂度 O(k^3)(三重循环:枚举长度组合+子序列生成+合并比较);
- 空间复杂度 O(k)(存储候选序列);
- 需处理 最大规模输入(
m=n=500,k=1000)。
-
边界处理:
- 空数组:若
nums1为空,则只能从nums2取k个元素(反之亦然); - 全零数组:如
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
输出:字典序最大的拼接序列(整数数组)。
解题思路
本题要求从两个数组 nums1 和 nums2 中保持数字相对顺序不变,选取数字组成长度为 k 的最大数。核心思路是枚举从 nums1 中选取 i 个数字、从 nums2 中选取 k-i 个数字的所有可能组合(i 的范围需满足 k-n <= i <= min(k, m)),然后对每种组合:
- 分别从
nums1和nums2中提取长度为i和k-i的最大子序列(使用单调栈算法)。 - 合并两个子序列,合并时采用贪心策略,每次选取当前字典序更大的子序列的头部元素。
- 比较所有合并后的序列,保留字典序最大的序列作为结果。
代码实现(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;
}
}
代码说明
-
主函数
maxNumber:- 初始化结果数组
res为-1(确保第一次比较时候选序列能覆盖它)。 - 计算
i的合理范围:start = max(0, k-n)到end = min(k, m)。 - 遍历所有可能的
i,生成候选序列并更新最大结果。
- 初始化结果数组
-
maxSubsequence函数:- 使用单调栈提取长度为
k的最大子序列。 - 遍历数组时,若当前元素大于栈顶元素且剩余元素足够填满栈,则弹出栈顶。
- 栈未满时压入当前元素。
- 使用单调栈提取长度为
-
merge函数:- 双指针遍历两个子序列,每次选取字典序更大的序列的头部元素。
- 使用
compare函数比较两个序列的字典序。
-
compare函数:- 比较两个数组从指定位置开始的字典序。
- 逐位比较元素,遇到不同元素返回差值。
- 前缀相同时,长度更长的序列字典序更大。
提交详情(执行用时、内存消耗)

1521

被折叠的 条评论
为什么被折叠?



