class Solution:
def removeDuplicateLetters(self, s: str) -> str:
last_index = {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_index[stack[-1]]:
seen.remove(stack.pop())
stack.append(c)
seen.add(c)
return ''.join(stack)
class Solution {
public:
string removeDuplicateLetters(string s) {
vector<int> lastIndex(26, -1);
for (int i = 0; i < s.size(); ++i)
lastIndex[s[i] - 'a'] = i;
vector<bool> seen(26, false);
string stack = "";
for (int i = 0; i < s.size(); ++i) {
char c = s[i];
if (seen[c - 'a']) continue;
while (!stack.empty() && c < stack.back() && i < lastIndex[stack.back() - 'a']) {
seen[stack.back() - 'a'] = false;
stack.pop_back();
}
stack.push_back(c);
seen[c - 'a'] = true;
}
return stack;
}
};
class Solution {
public String removeDuplicateLetters(String s) {
int[] lastIndex = new int[26];
for (int i = 0; i < s.length(); i++)
lastIndex[s.charAt(i) - 'a'] = i;
boolean[] seen = new boolean[26];
StringBuilder stack = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (seen[c - 'a']) continue;
while (stack.length() > 0 && c < stack.charAt(stack.length() - 1)
&& i < lastIndex[stack.charAt(stack.length() - 1) - 'a']) {
seen[stack.charAt(stack.length() - 1) - 'a'] = false;
stack.deleteCharAt(stack.length() - 1);
}
stack.append(c);
seen[c - 'a'] = true;
}
return stack.toString();
}
}
var removeDuplicateLetters = function(s) {
const lastIndex = {};
for (let i = 0; i < s.length; i++) {
lastIndex[s[i]] = i;
}
const stack = [];
const seen = new Set();
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (seen.has(c)) continue;
while (stack.length && c < stack[stack.length - 1] && i < lastIndex[stack[stack.length - 1]]) {
seen.delete(stack.pop());
}
stack.push(c);
seen.add(c);
}
return stack.join('');
};
Given a string s
consisting of only lowercase English letters, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.
Example:
Input: s = "bcabc"
Output: "abc"
At first glance, it seems we could simply remove duplicates by using a set. However, this would not guarantee the smallest lexicographical order. For example, for s = "cbacdcbc"
, a set would give us "cbad"
(in order of appearance), but the correct answer is "acdb"
.
The brute-force approach would be to generate all unique permutations of the string's letters and pick the lexicographically smallest one. But this is highly inefficient, especially for longer strings.
We need to find a way to always pick the smallest possible character at every step, while ensuring that all other characters can still appear later if we skip them now. This leads us to a greedy and stack-based approach.
The optimized solution uses a monotonic stack and keeps track of the last occurrence of each character. Here is how it works:
This method ensures that we always get the smallest lexicographical order, use each letter only once, and preserve the relative order as much as possible.
Let's walk through the input s = "cbacdcbc"
:
"acdb"
.
The key insight is to use a greedy, stack-based approach: always pick the smallest character possible while ensuring that all other necessary characters can still be chosen later. By tracking last occurrences and using a stack to maintain order, we efficiently build the smallest lexicographical string with unique letters. This method is elegant, fast, and leverages the properties of the problem for an optimal solution.