Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Program to find the most competitive subsequence in Python
A competitive subsequence is a subsequence where elements appear in their original order from the array. Given an array and a target size k, we need to find the most competitive subsequence of size k. A subsequence s1 is more competitive than s2 if at the first differing position, s1 has a smaller number than s2.
For example, given nums = [4,6,3,7] and k = 2, among all possible subsequences of size 2: [4,6], [4,3], [4,7], [6,3], [6,7], [3,7], the subsequence [3,7] is most competitive.
Algorithm
We use a greedy approach with a stack to build the most competitive subsequence ?
- Calculate
attempts= total elements we can remove (size of nums - k) - Use a stack to build our result
- For each number, remove larger elements from stack top if we have removal attempts left
- Add current number to stack
- Return first k elements of stack
Implementation
def solve(nums, k):
attempts = len(nums) - k
stack = []
for num in nums:
while stack and num < stack[-1] and attempts > 0:
stack.pop()
attempts -= 1
stack.append(num)
return stack[:k]
# Example 1
nums = [4, 6, 3, 7]
k = 2
result = solve(nums, k)
print(f"Input: {nums}, k = {k}")
print(f"Output: {result}")
Input: [4, 6, 3, 7], k = 2 Output: [3, 7]
Step-by-Step Trace
Let's trace through the algorithm with nums = [4,6,3,7] and k = 2 ?
def solve_with_trace(nums, k):
attempts = len(nums) - k
stack = []
print(f"Initial: attempts = {attempts}, stack = {stack}")
for i, num in enumerate(nums):
print(f"\nStep {i+1}: Processing {num}")
while stack and num < stack[-1] and attempts > 0:
removed = stack.pop()
attempts -= 1
print(f" Removed {removed}, attempts left: {attempts}")
stack.append(num)
print(f" Added {num}, stack: {stack}")
result = stack[:k]
print(f"\nFinal result: {result}")
return result
nums = [4, 6, 3, 7]
k = 2
solve_with_trace(nums, k)
Initial: attempts = 2, stack = [] Step 1: Processing 4 Added 4, stack: [4] Step 2: Processing 6 Added 6, stack: [4, 6] Step 3: Processing 3 Removed 6, attempts left: 1 Removed 4, attempts left: 0 Added 3, stack: [3] Step 4: Processing 7 Added 7, stack: [3, 7] Final result: [3, 7]
More Examples
def solve(nums, k):
attempts = len(nums) - k
stack = []
for num in nums:
while stack and num < stack[-1] and attempts > 0:
stack.pop()
attempts -= 1
stack.append(num)
return stack[:k]
# Example 2: Already optimal
nums2 = [1, 2, 3, 4]
k2 = 2
print(f"Input: {nums2}, k = {k2}")
print(f"Output: {solve(nums2, k2)}")
# Example 3: Larger array
nums3 = [9, 8, 7, 6, 5, 4, 3, 2, 1]
k3 = 3
print(f"Input: {nums3}, k = {k3}")
print(f"Output: {solve(nums3, k3)}")
Input: [1, 2, 3, 4], k = 2 Output: [1, 2] Input: [9, 8, 7, 6, 5, 4, 3, 2, 1], k = 3 Output: [1, 2, 3]
Time and Space Complexity
- Time Complexity: O(n) where n is the length of nums. Each element is pushed and popped at most once.
- Space Complexity: O(k) for the stack that stores at most k elements in the final result.
Conclusion
The greedy stack approach efficiently finds the most competitive subsequence by removing larger elements when possible. The algorithm ensures we maintain the original order while selecting the lexicographically smallest subsequence of size k.
