Create Maximum Number

From two digit arrays, pick subsequences of lengths summing to k and merge them into the lexicographically largest k-digit number.

Hard
Time O(k * (n + m + k)^2) Space O(k)
LeetCode
8 min read
two-pointers monotonic-stack greedy

Problem#

Given digit arrays nums1 (length n) and nums2 (length m), pick a length-i subsequence of nums1 and a length-(k-i) subsequence of nums2. Merge them preserving each subsequence’s order so the resulting k-digit number is lexicographically largest. Return the digits.

Examples#

  • nums1 = [3,4,6,5], nums2 = [9,1,2,5,8,3], k = 5[9,8,6,5,3]

Constraints#

  • 1 <= n, m, 1 <= k <= n + m, digits in [0, 9].

Hints#

Hint 1
For each split i, separately compute the largest length-i subsequence of nums1 via monotonic stack, do the same for nums2, then merge.
Hint 2
Merging on ties needs lookahead: pick the side whose remaining suffix is lex-larger.

Approach#

Three primitives:

  1. maxSubsequence(arr, t): largest length-t subsequence — monotonic stack, popping smaller predecessors while we still have leftover digits.
  2. merge(a, b): merge two arrays into the lex-largest result, breaking ties by suffix comparison.
  3. Try every i ∈ [max(0,k-m), min(k,n)], combine, keep the best.

Solution#

Create Maximum Number
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
const int n = nums1.size(), m = nums2.size();
vector<int> best;
for (int i = max(0, k - m); i <= min(k, n); ++i) {
auto a = pickMax(nums1, i);
auto b = pickMax(nums2, k - i);
auto cand = merge(a, b);
if (cand > best) best = move(cand);
}
return best;
}
private:
vector<int> pickMax(const vector<int>& a, int t) {
vector<int> stk; stk.reserve(t);
int drop = a.size() - t;
for (int x : a) {
while (!stk.empty() && drop > 0 && stk.back() < x) {
stk.pop_back(); --drop;
}
stk.push_back(x);
}
stk.resize(t);
return stk;
}
vector<int> merge(const vector<int>& a, const vector<int>& b) {
vector<int> out; out.reserve(a.size() + b.size());
int i = 0, j = 0;
while (i < (int)a.size() || j < (int)b.size()) {
if (greaterSuffix(a, i, b, j)) out.push_back(a[i++]);
else out.push_back(b[j++]);
}
return out;
}
bool greaterSuffix(const vector<int>& a, int i, const vector<int>& b, int j) {
while (i < (int)a.size() && j < (int)b.size() && a[i] == b[j]) { ++i; ++j; }
if (j == (int)b.size()) return true;
if (i == (int)a.size()) return false;
return a[i] > b[j];
}
};

Editorial#

Decomposing into “best subsequence per array” + “best merge” is the canonical reduction. pickMax is monotonic-stack with an explicit “drop budget” — we pop a smaller top only if we can afford to drop it (drop > 0). Merging is not a straight max-of-fronts because ties matter: when a[i] == b[j], the correct next digit is the one whose suffix is lex-larger, so we compare entire remaining segments.

Complexity#

  • Time: O(k · (n + m + k)²) — outer split × merge cost.
  • Space: O(k).

Concept revision#

Search ESC

Keyboard shortcuts

Shortcuts are disabled while typing in inputs.