Find the Most Competitive Subsequence

Updated on 28 May, 2025
Find the Most Competitive Subsequence header image

Problem Statement

Given an integer array nums and a positive integer k, the task is to return the most competitive subsequence of nums that has a size of k. A subsequence in this context is formed by erasing some elements (possibly none) from the array such that the remaining elements preserve their original order. A subsequence a is deemed more competitive than another subsequence b, if, at the first point of difference, the element in a is smaller than the corresponding element in b. This problem involves identifying the lexicographically smallest subsequence of a given length from the array.

Examples

Example 1

Input:

nums = [3,5,2,6], k = 2

Output:

[2,6]

Explanation:

Among the set of every possible subsequence: {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]}, [2,6] is the most competitive.

Example 2

Input:

nums = [2,4,3,3,5,4,9,6], k = 4

Output:

[2,3,3,4]

Constraints

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109
  • 1 <= k <= nums.length

Approach and Intuition

The key to solving this problem lies in understanding the concept of "most competitive subsequence". Essentially, for a subsequence to be competitive, it should be the smallest lexicographical sequence possible of the given length k from the array. We can approach this problem using a greedy strategy and a stack to help maintain the required order of elements:

  1. Initialize an empty stack.
  2. Loop through each element in the array:
    • While there is any element left in the stack AND the last element in the stack is greater than the current element from the array AND the stack size plus the remaining elements in the array is still greater than or equal to k, pop the stack. This step ensures that we always have the smallest possible value at the current position for our result.
    • Push the current element on the stack if the size of the stack is less than k.
  3. The stack essentially represents the most competitive subsequence of length k.

This method leverages the stack’s LIFO mechanism to maintain the smallest possible values in sequence and ensures that larger numbers are only kept when absolutely necessary to reach the size k.

Solutions

  • C++
  • Java
cpp
class Solution {
public:
    vector<int> findCompetitiveNumbers(vector<int>& inputNumbers, int requiredLength) {
        deque<int> decreasingSequence;
        int elementsToRemove = inputNumbers.size() - requiredLength;
        for (int index = 0; index < inputNumbers.size(); index++) {
            while (!decreasingSequence.empty() && decreasingSequence.back() > inputNumbers[index] &&
                   elementsToRemove > 0) {
                decreasingSequence.pop_back();
                elementsToRemove--;
            }
            decreasingSequence.push_back(inputNumbers[index]);
        }
        vector<int> competitiveSubset;
        for (int count = 0; count < requiredLength; count++) {
            competitiveSubset.push_back(decreasingSequence.front());
            decreasingSequence.pop_front();
        }
        return competitiveSubset;
    }
};

The provided C++ code implements a function to find the most competitive subsequence from a given list of integers. Here's a breakdown of the solution:

  • The function named findCompetitiveNumbers accepts a vector of integers inputNumbers and an integer requiredLength, representing the desired length of the subsequence.
  • It utilizes a double-ended queue (deque<int>) named decreasingSequence to maintain elements of a potential competitive subsequence in a decreasing order, ensuring the smallest possible numbers are included.
  • The main computation involves iterating over the inputNumbers. For each number:
    • If the last element in decreasingSequence is greater than the current number, and there are still elements that can be removed (elementsToRemove is greater than 0), the back of the deque is popped. This step essentially keeps the deque minimal by retaining competitive elements.
    • The current number is then added to decreasingSequence.
  • After processing all input numbers, the top requiredLength elements of decreasingSequence are transferred to a vector called competitiveSubset. This vector represents the most competitive subsequence and is returned as the result.

This approach ensures that the resultant subsequence is, lexicographically, the smallest possible subsequence of the required length. By effectively managing the deque and tracking the number of allowable removals, the solution remains efficient and direct.

java
class Solution {
    public int[] smallestSubsequence(int[] data, int k) {
        Deque<Integer> minStack = new ArrayDeque<Integer>();
        int removeOperations = data.length - k;
        for (int i = 0; i < data.length; i++) {
            while (!minStack.isEmpty() && minStack.peekLast() > data[i] && removeOperations > 0) {
                minStack.pollLast();
                removeOperations--;
            }
            minStack.addLast(data[i]);
        }
        int[] output = new int[k];
        for (int i = 0; i < k; i++) {
            output[i] = minStack.pollFirst();
        }
        return output;
    }
}

The Java program provided is designed to find the most competitive subsequence of a given length k from an array data. The algorithm optimizes the sequence such that it is the lexicographically smallest possible subsequence of the specified length. The core data structure used in this implementation is a deque (Deque<Integer>) which helps in efficiently managing the elements during the selection of the subsequence.

Here's a breakdown of how the code works:

  • A deque (minStack) is used to keep potential candidates of the smallest subsequence.
  • The variable removeOperations calculates how many elements can be removed to attain a subsequence of length k.
  • A loop iterates through each element of the array data.
    • Inside the loop, there's a nested while loop which checks if the last element in the minStack can be removed (i.e., if it's greater than the current element in data and there are still allowable remove operations). This ensures that the sequence in the deque remains the smallest lexicographically.
    • The current element from data is then added to minStack.
  • After processing all elements in data, the first k elements of minStack are transferred to the output array output.
  • The output array, containing the smallest lexicographical subsequence of length k, is then returned.

In summary, this solution is effective in computing the desired subsequence due to its utilization of a deque which facilitates the maintenance of order while allowing for necessary removals, ensuring the resulting subsequence is the smallest possible. This method is both time-efficient and straightforward, leveraging simple iteration and condition checking to build the solution.

Comments

No comments yet.