You are given a string s
, an integer k
, a letter letter
, and an integer repetition
.
Return the lexicographically smallest subsequence of s
of length k
that has the letter letter
appear at least repetition
times. The test cases are generated so that the letter
appears in s
at least repetition
times.
A subsequence is a string that can be derived from another string by deleting some or no characters without changing the order of the remaining characters.
A string a
is lexicographically smaller than a string b
if in the first position where a
and b
differ, string a
has a letter that appears earlier in the alphabet than the corresponding letter in b
.
Example 1:
Input: s = "leet", k = 3, letter = "e", repetition = 1 Output: "eet" Explanation: There are four subsequences of length 3 that have the letter 'e' appear at least 1 time: - "lee" (from "leet") - "let" (from "leet") - "let" (from "leet") - "eet" (from "leet") The lexicographically smallest subsequence among them is "eet".
Example 2:
Input: s = "leetcode", k = 4, letter = "e", repetition = 2 Output: "ecde" Explanation: "ecde" is the lexicographically smallest subsequence of length 4 that has the letter "e" appear at least 2 times.
Example 3:
Input: s = "bb", k = 2, letter = "b", repetition = 2 Output: "bb" Explanation: "bb" is the only subsequence of length 2 that has the letter "b" appear at least 2 times.
Constraints:
1 <= repetition <= k <= s.length <= 5 * 104
s
consists of lowercase English letters.letter
is a lowercase English letter, and appears in s
at least repetition
times.This problem asks to find the lexicographically smallest subsequence of length k
from a given string s
containing the letter letter
at least repetition
times. The solution employs a greedy approach using a monotonic stack.
Algorithm:
Initialization: Create a stack stack
to store the characters of the subsequence, and an integer letterCount
to track the occurrences of letter
in the subsequence.
Iteration: Iterate through the string s
. For each character c
:
Case 1: c
is not letter
: If the stack has less than k - repetition
characters, and c
is smaller than the top of the stack (if the stack is not empty), pop the top element from the stack and push c
. This ensures that we are always selecting smaller characters whenever possible to maintain lexicographical order. If the stack has k-repetition
characters already or if c is not smaller than the top of the stack, we push c to the stack only if the number of elements in the stack is less than k.
Case 2: c
is letter
: Push c
to the stack. Increment letterCount
.
Post-processing: If the stack has less than k
elements, append the remaining characters from the input string that are lexicographically smaller than the top of the stack to get a subsequence of length k.
Return: Convert the stack into a string and return it.
Time Complexity Analysis:
The algorithm iterates through the string s
once, and the stack operations (push and pop) take constant time. The overall time complexity is O(N), where N is the length of the string s
.
Space Complexity Analysis:
The space complexity is O(k) due to the stack which stores at most k
characters.
def smallestSubsequence(s, k, letter, repetition):
stack = []
letterCount = 0
for c in s:
while stack and stack[-1] > c and len(stack) - (c == letter) + len(s) - s.index(c) -1 >= k - repetition and letterCount < repetition:
if stack[-1] == letter:
letterCount -= 1
stack.pop()
if len(stack) < k:
if c == letter:
letterCount += 1
stack.append(c)
return "".join(stack)
import java.util.Stack;
class Solution {
public String smallestSubsequence(String s, int k, char letter, int repetition) {
Stack<Character> stack = new Stack<>();
int letterCount = 0;
for (char c : s.toCharArray()) {
while (!stack.isEmpty() && stack.peek() > c && stack.size() - (stack.peek() == letter ? 1 : 0) + s.length() - s.indexOf(c) - 1 >= k - repetition && letterCount < repetition) {
if (stack.pop() == letter) {
letterCount--;
}
}
if (stack.size() < k) {
if (c == letter) {
letterCount++;
}
stack.push(c);
}
}
return String.valueOf(stack.toArray()).replaceAll(", ", "");
}
}
#include <string>
#include <stack>
using namespace std;
string smallestSubsequence(string s, int k, char letter, int repetition) {
stack<char> st;
int letterCount = 0;
for (char c : s) {
while (!st.empty() && st.top() > c && st.size() - (st.top() == letter) + s.length() - s.find(c) - 1 >= k - repetition && letterCount < repetition) {
if (st.top() == letter) letterCount--;
st.pop();
}
if (st.size() < k) {
if (c == letter) letterCount++;
st.push(c);
}
}
string res = "";
while (!st.empty()) {
res = st.top() + res;
st.pop();
}
return res;
}
import (
"bytes"
"strings"
)
func smallestSubsequence(s string, k int, letter byte, repetition int) string {
stack := []byte{}
letterCount := 0
for i := range s {
c := s[i]
for len(stack) > 0 && stack[len(stack)-1] > c && len(stack)-(byte(stack[len(stack)-1])==letter) + len(s)-i-1 >=k-repetition && letterCount < repetition {
if stack[len(stack)-1] == letter {
letterCount--
}
stack = stack[:len(stack)-1]
}
if len(stack) < k {
if c == letter {
letterCount++
}
stack = append(stack, c)
}
}
return string(stack)
}
The provided codes implement the greedy algorithm described above. They differ slightly in syntax and handling of string manipulation but share the core logic. Remember to handle edge cases carefully in your implementation, especially scenarios where the stack becomes empty during the iteration.