class Solution:
def smallestSubsequence(self, s: str) -> str:
last_occ = {c: i for i, c in enumerate(s)}
stack = []
seen = set()
for i, c in enumerate(s):
if c in seen:
continue
while stack and c < stack[-1] and i < last_occ[stack[-1]]:
seen.remove(stack.pop())
stack.append(c)
seen.add(c)
return ''.join(stack)
class Solution {
public:
string smallestSubsequence(string s) {
vector<int> last_occ(256, -1);
for (int i = 0; i < s.length(); ++i)
last_occ[s[i]] = i;
vector<bool> seen(256, false);
string stack = "";
for (int i = 0; i < s.length(); ++i) {
char c = s[i];
if (seen[c]) continue;
while (!stack.empty() && c < stack.back() && i < last_occ[stack.back()]) {
seen[stack.back()] = false;
stack.pop_back();
}
stack.push_back(c);
seen[c] = true;
}
return stack;
}
};
class Solution {
public String smallestSubsequence(String s) {
int[] last = new int[256];
for (int i = 0; i < s.length(); ++i)
last[s.charAt(i)] = i;
boolean[] seen = new boolean[256];
StringBuilder stack = new StringBuilder();
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (seen[c]) continue;
while (stack.length() > 0 && c < stack.charAt(stack.length() - 1) && i < last[stack.charAt(stack.length() - 1)]) {
seen[stack.charAt(stack.length() - 1)] = false;
stack.deleteCharAt(stack.length() - 1);
}
stack.append(c);
seen[c] = true;
}
return stack.toString();
}
}
var smallestSubsequence = function(s) {
const last = {};
for (let i = 0; i < s.length; ++i) last[s[i]] = i;
const stack = [];
const seen = new Set();
for (let i = 0; i < s.length; ++i) {
let c = s[i];
if (seen.has(c)) continue;
while (
stack.length > 0 &&
c < stack[stack.length - 1] &&
i < last[stack[stack.length - 1]]
) {
seen.delete(stack.pop());
}
stack.push(c);
seen.add(c);
}
return stack.join('');
};
Given a string s
, return the lexicographically smallest subsequence of s
that contains all the distinct characters of s
exactly once.
s
exactly once (no repeats).s
, but must keep the original order of the remaining characters.
Constraints:
1 <= s.length <= 1000
s
consists of lowercase English letters.
At first glance, you might consider generating all subsequences of s
that contain each unique character exactly once, and then picking the smallest one in lexicographical order. However, this brute-force approach is infeasible due to the exponential number of possible subsequences.
Instead, we need a way to efficiently build the answer by making local, greedy choices: at each step, decide whether to keep or remove the current character, ensuring that the final subsequence is the smallest possible and contains all unique letters exactly once.
The key insight is that we want the leftmost possible character at each position, but only if we can still include all other required characters later on. So, we need to know if it's safe to remove a character now (because it will appear again later), and we must avoid duplicates.
To solve this efficiently, we use a monotonic stack and a last occurrence map:
s
, record the last index where it appears. This helps us know if it's safe to remove a character from our current answer, knowing we can add it back later.
We use a set (or boolean array) to track which characters are currently in the stack for O(1) lookups.
Let's walk through the example s = "cbacdcbc"
:
c:7, b:6, a:2, d:5
c
.
b < c
and c
appears again at 7, so pop c
. Add b
.
a < b
and b
appears again at 6, so pop b
. Add a
.
c
(not in stack).
d
.
b
not in stack. b < d
and d
does not appear again, so can't pop d
. Add b
.
The stack is [a, c, d, b]
. But since we always add to the end, the final answer is acdb
.
s
.
The problem asks us to efficiently find the lexicographically smallest subsequence containing all unique characters from a given string, in order. The stack-based greedy approach, guided by last occurrences and a seen set, allows us to make optimal local decisions that guarantee the global minimum result. This method is both elegant and efficient, running in linear time and space.