AlgoMaster Logo

Find the Most Competitive Subsequence

mediumFrequencyUpdated September 21, 2026

Understanding the Problem

We need to pick exactly k elements from nums while keeping their original relative order, and the resulting subsequence should be the lexicographically smallest possible. When two subsequences of the same length are compared, the more competitive one has the smaller value at the first position where they differ.

This is a greedy selection problem with a constraint. At each position in the result we want the smallest possible value, but we also have to leave enough elements remaining to fill the rest of the subsequence. Picking the smallest value now and keeping enough elements for later pull in opposite directions, and balancing the two is the core of the problem.

That balance maps onto building a stack that stays as small as possible at the front, subject to a capacity of k. When a smaller element arrives, we can remove larger elements already chosen, as long as enough elements remain in the array to complete the subsequence.

Key Constraints:

  • 1 <= nums.length <= 10^5 means an O(n^2) approach will time out. The target is O(n).
  • 0 <= nums[i] <= 10^9 means values fit in a 32-bit signed integer, and there are no negatives. The range is too wide for counting sort or a frequency array.
  • 1 <= k <= nums.length guarantees k is always valid, so there is no k > n case to handle.

Approach 1: Brute Force (Generate All Subsequences)

Intuition

Generate every possible subsequence of size k, compare them lexicographically, and return the smallest one. This enumerates all candidates and picks the winner with no cleverness.

For an array of size n, the number of subsequences of size k is C(n, k), which grows exponentially. For small inputs it works and confirms what the answer should be, which is useful for checking the optimal approach against it.

Algorithm

  1. Use backtracking to generate all subsequences of size k from nums.
  2. For each subsequence, compare it with the current best using lexicographic comparison.
  3. Keep track of the lexicographically smallest subsequence found.
  4. Return the result after checking all candidates.

Example Walkthrough

Input:

0
3
1
5
2
2
3
6
nums

All subsequences of size 2: [3,5], [3,2], [3,6], [5,2], [5,6], [2,6]

Comparing lexicographically: [2,6] is the smallest (2 < 3 < 5 at the first position).

Result:

0
2
1
6
result

Code

The brute force enumerates an exponential number of subsequences, most of which are far from optimal. The next approach builds the answer greedily in a single pass, always preferring a smaller value at an earlier position whenever there is still room to complete the subsequence.

Approach 2: Monotonic Stack (Optimal)

Intuition

Build the result left to right. While scanning nums, when a value is smaller than one already chosen, replacing the larger earlier pick with this smaller value produces a more competitive subsequence. The replacement is only allowed when enough elements remain in the array to still fill all k spots.

A monotonic stack implements this directly. The stack holds the current best subsequence so far. For each element in the array:

  • If the current element is smaller than the top of the stack, and enough elements remain to still fill k positions, pop the top (it is a larger value at an earlier position, so removing it helps).
  • Push the current element onto the stack, unless the stack already holds k elements.

The remaining-elements check is what keeps the answer valid. With n - i elements left in the array (including the current one) and stack.size() elements already chosen, a pop is allowed only when stack.size() - 1 + (n - i) >= k. After removing one element, the elements still on the stack plus the elements still ahead in the array must be enough to reach size k.

Algorithm

  1. Initialize an empty stack (or list used as a stack).
  2. Iterate through each element nums[i]:
    • While the stack is not empty, AND the top of the stack is greater than nums[i], AND stack.size() - 1 + (n - i) >= k (we can afford to pop): pop the top of the stack.
    • If the stack size is less than k, push nums[i] onto the stack.
  3. Return the stack as the result array.

Example Walkthrough

1i=0: nums[0]=2, stack empty, push 2
0
2
i
1
4
2
3
3
3
4
5
5
4
6
9
7
6
1/8
1Push 2
2
Top
1/8

Code