Talentd
FleetCode
Make and follow sheets.Sign in

Remove Duplicate Letters solution and explanation

MediumGreedyMonotonic StackStackString6 min read54.7% acceptanceUpdated 9 Apr 2026
Remove Duplicate Letters | Leetcode #316Techdose · 14:55 · 37K viewsWatch on YouTube
9 more videos on this problem
  1. Remove Duplicate Letters | Leetcode #316Techdose · 14:55 · 37K views
  2. Remove Duplicate Letters | Why Monotonic Stack | Intuition | Leetcode - 316 | Same as Leetcode 1081codestorywithMIK · 23:04 · 29K views
  3. remove duplicate letters | leetcode 316 | smallest subsequence of distinct characters| leetcode 1081Naresh Gupta · 9:25 · 29K views
  4. Remove Duplicate Letters | Leetcode 316 | Stack | Day-18Ayushi Sharma · 21:00 · 14K views
  5. 316. Remove Duplicate Letters | Day 015 | StackAryan Mittal · 13:36 · 12K views
  6. Remove Duplicate Letters | LeetCode 316 & 1081 | October LeetCode Challenge | Day 11Learn Code Repeat · 16:47 · 8K views
  7. Remove Duplicate Letters | Leet code 316 | Theory explained + Python codeSai Anish Malla · 17:32 · 6K views
  8. Leetcode - Remove Duplicate Letters (Python)Timothy H Chang · 6:31 · 5K views
  9. REMOVE DUPLICATE LETTERS | LEETCODE 316 | PYTHON STACK SOLUTIONCracking FAANG · 18:15 · 5K views
  10. Remove Duplicate Letters | Leetcode 316 | Stacks | Live coding session 🔥🔥Coding Decoded · 14:46 · 3K views

The problem

Given a string s, 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 1:

Input: s = "bcabc"
Output: "abc"

Example 2:

Input: s = "cbacdcbc"
Output: "acdb"

Constraints:

  • 1 <= s.length <= 104
  • s consists of lowercase English letters.

Note: This question is the same as 1081: https://leetcode.com/problems/smallest-subsequence-of-distinct-characters/

Approach overview

Problem Overview: Given a string s, remove duplicate characters so every letter appears exactly once while keeping the result as the smallest possible lexicographical string. The relative ordering constraints of the original string still matter, so you cannot freely reorder characters.

Approach 1: Greedy and Stack (O(n) time, O(1) space)

This approach uses a stack to build the result while maintaining lexicographical order. First record the last occurrence index of every character. Then iterate through the string and maintain a visited set. For each character, pop elements from the stack while the current character is smaller than the stack top and the stack top appears later in the string. This greedy decision ensures the final string remains lexicographically minimal. The stack effectively becomes a monotonic stack that enforces increasing order when possible.

Approach 2: Iterative Result String Construction (O(n) time, O(1) space)

This method builds the answer string directly instead of using an explicit stack structure. Iterate through characters and maintain a result string along with a visited map. When a new character arrives, repeatedly remove the last character from the result if it is lexicographically larger and appears again later in the string. Then append the current character and mark it as used. This follows the same greedy principle as the stack approach but uses string operations instead of a dedicated stack data structure. The algorithm still scans the input once and uses constant extra memory for character tracking.

Recommended for interviews: The greedy stack solution is the expected answer. Interviewers want to see that you track the last occurrence of each character and maintain a lexicographically minimal structure while iterating once through the string. Understanding how the greedy decision interacts with stack popping demonstrates strong algorithmic reasoning. The iterative string version shows the same logic but the explicit stack version is easier to explain during interviews.

Approach 1: Approach 1: Greedy and Stack

This approach uses a stack to build the resultant string such that it is the smallest lexicographical order. We will also use an array to keep count of each character’s frequency and a boolean array to track the characters that have been added to the stack. As we iterate over each character, we decide whether to add it to the stack or skip it based on the frequency and lexicographical conditions.

def removeDuplicateLetters(s: str) -> str:
    from collections import Counter
    freq = Counter(s)
    stack = []
    in_stack = set()

    for char in s:
        freq[char] -= 1
        if char in in_stack:
            continue
        while stack and stack[-1] > char and freq[stack[-1]] > 0:
            in_stack.remove(stack.pop())
        stack.append(char)
        in_stack.add(char)

    return ''.join(stack)

print(removeDuplicateLetters("cbacdcbc"))
import java.util.Stack;

public class Solution {
    public String removeDuplicateLetters(String s) {
        int[] freq = new int[26];
        boolean[] inStack = new boolean[26];
        for (char c : s.toCharArray()) freq[c - 'a']++;
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            freq[c - 'a']--;
            if (inStack[c - 'a']) continue;
            while (!stack.isEmpty() && stack.peek() > c && freq[stack.peek() - 'a'] > 0) {
                inStack[stack.pop() - 'a'] = false;
            }
            stack.push(c);
            inStack[c - 'a'] = true;
        }
        StringBuilder res = new StringBuilder();
        for (char c : stack) res.append(c);
        return res.toString();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.removeDuplicateLetters("cbacdcbc"));
    }
}
#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;

string removeDuplicateLetters(string s) {
    vector<int> freq(26, 0);
    vector<bool> inStack(26, false);
    for (char c : s) freq[c - 'a']++;
    stack<char> result;
    for (char c : s) {
        freq[c - 'a']--;
        if (inStack[c - 'a']) continue;
        while (!result.empty() && result.top() > c && freq[result.top() - 'a'] > 0) {
            inStack[result.top() - 'a'] = false;
            result.pop();
        }
        result.push(c);
        inStack[c - 'a'] = true;
    }
    string res;
    while (!result.empty()) {
        res = result.top() + res;
        result.pop();
    }
    return res;
}

int main() {
    string s = "cbacdcbc";
    cout << removeDuplicateLetters(s) << endl;
    return 0;
}
function removeDuplicateLetters(s) {
    const freq = new Array(26).fill(0);
    const inStack = new Array(26).fill(false);
    for (let char of s) {
        freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++;
    }
    const stack = [];
    for (let char of s) {
        freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]--;
        if (inStack[char.charCodeAt(0) - 'a'.charCodeAt(0)]) continue;
        while (stack.length > 0 && stack[stack.length - 1] > char && freq[stack[stack.length - 1].charCodeAt(0) - 'a'.charCodeAt(0)] > 0) {
            inStack[stack.pop().charCodeAt(0) - 'a'.charCodeAt(0)] = false;
        }
        stack.push(char);
        inStack[char.charCodeAt(0) - 'a'.charCodeAt(0)] = true;
    }
    return stack.join('');
}

console.log(removeDuplicateLetters("cbacdcbc"));
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

char* removeDuplicateLetters(char* s) {
    int n = strlen(s);
    int freq[26] = {0};
    bool inStack[26] = {false};
    for (int i = 0; i < n; i++) freq[s[i] - 'a']++;
    char* stack = (char*)malloc((n + 1) * sizeof(char));
    int top = -1;
    for (int i = 0; i < n; i++) {
        freq[s[i] - 'a']--;
        if (inStack[s[i] - 'a']) continue;
        while (top >= 0 && stack[top] > s[i] && freq[stack[top] - 'a'] > 0) {
            inStack[stack[top] - 'a'] = false;
            top--;
        }
        stack[++top] = s[i];
        inStack[s[i] - 'a'] = true;
    }
    stack[++top] = '\0';
    return stack;
}

int main() {
    char s[] = "cbacdcbc";
    printf("%s\n", removeDuplicateLetters(s));
    return 0;
}
using System;
using System.Collections.Generic;

public class Solution {
    public string RemoveDuplicateLetters(string s) {
        int[] freq = new int[26];
        bool[] inStack = new bool[26];
        foreach (char c in s) freq[c - 'a']++;
        Stack<char> stack = new Stack<char>();
        foreach (char c in s) {
            freq[c - 'a']--;
            if (inStack[c - 'a']) continue;
            while (stack.Count > 0 && stack.Peek() > c && freq[stack.Peek() - 'a'] > 0) {
                inStack[stack.Pop() - 'a'] = false;
            }
            stack.Push(c);
            inStack[c - 'a'] = true;
        }
        char[] result = stack.ToArray();
        Array.Reverse(result);
        return new string(result);
    }

    public static void Main(string[] args) {
        Solution solution = new Solution();
        Console.WriteLine(solution.RemoveDuplicateLetters("cbacdcbc"));
    }
}

The Python implementation uses a stack to keep track of the correct order and a set to check if a character is already in the stack. The Counter from the collections module helps in efficiently counting character frequencies.

Complexity

Time Complexity: O(n), where n is the length of the string.
Space Complexity: O(1) as the maximum number of unique characters in a stack is 26.

Try this approach in the editor

Approach 2: Approach 2: Iterative with Result String Construction

This approach focuses on iteratively constructing the result string while ensuring that the result remains lexicographically smallest by checking each character and including it in the result only if it meets certain frequency and order criteria.

def removeDuplicateLetters(s: str) -> str:
    freq = {char: s.count(char) for char in set(s)}
    result = []
    used = set()

    for char in s:
        freq[char] -= 1
        if char in used:
            continue
        while result and result[-1] > char and freq[result[-1]] > 0:
            used.remove(result.pop())
        result.append(char)
        used.add(char)

    return ''.join(result)

print(removeDuplicateLetters("cbacdcbc"))
public class Solution {
    public String removeDuplicateLetters(String s) {
        int[] freq = new int[26];
        boolean[] used = new boolean[26];
        for (char c : s.toCharArray()) freq[c - 'a']++;
        StringBuilder res = new StringBuilder();
        for (char c : s.toCharArray()) {
            freq[c - 'a']--;
            if (used[c - 'a']) continue;
            while (res.length() > 0 && res.charAt(res.length() - 1) > c && freq[res.charAt(res.length() - 1) - 'a'] > 0) {
                used[res.charAt(res.length() - 1) - 'a'] = false;
                res.deleteCharAt(res.length() - 1);
            }
            res.append(c);
            used[c - 'a'] = true;
        }
        return res.toString();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.removeDuplicateLetters("cbacdcbc"));
    }
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;

string removeDuplicateLetters(const string& s) {
    vector<int> freq(26, 0);
    vector<bool> used(26, false);
    for (char c : s) freq[c - 'a']++;
    string res;
    for (char c : s) {
        freq[c - 'a']--;
        if (used[c - 'a']) continue;
        while (!res.empty() && res.back() > c && freq[res.back() - 'a'] > 0) {
            used[res.back() - 'a'] = false;
            res.pop_back();
        }
        res += c;
        used[c - 'a'] = true;
    }
    return res;
}

int main() {
    string s = "cbacdcbc";
    cout << removeDuplicateLetters(s) << endl;
    return 0;
}
function removeDuplicateLetters(s) {
    const freq = Array(26).fill(0);
    const used = Array(26).fill(false);
    for (const char of s) {
        freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++;
    }
    const result = [];
    for (const char of s) {
        const index = char.charCodeAt(0) - 'a'.charCodeAt(0);
        freq[index]--;
        if (used[index]) continue;
        while (result.length && result[result.length - 1] > char && freq[result[result.length - 1].charCodeAt(0) - 'a'.charCodeAt(0)]) {
            used[result.pop().charCodeAt(0) - 'a'.charCodeAt(0)] = false;
        }
        result.push(char);
        used[index] = true;
    }
    return result.join('');
}

console.log(removeDuplicateLetters("cbacdcbc"));
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

char* removeDuplicateLetters(char* s) {
    int len = strlen(s);
    int freq[26] = {0};
    bool used[26] = {false};
    for (int i = 0; i < len; i++) freq[s[i] - 'a']++;
    int pos = 0;
    char* result = (char*)malloc((26 + 1) * sizeof(char));
    for (int i = 0; i < len; i++) {
        freq[s[i] - 'a']--;
        if (!used[s[i] - 'a']) {
            while (pos > 0 && result[pos - 1] > s[i] && freq[result[pos - 1] - 'a'] > 0) {
                used[result[--pos] - 'a'] = false;
            }
            result[pos++] = s[i];
            used[s[i] - 'a'] = true;
        }
    }
    result[pos] = '\0';
    return result;
}

int main() {
    char s[] = "cbacdcbc";
    printf("%s\n", removeDuplicateLetters(s));
    return 0;
}
using System;
using System.Collections.Generic;

public class Solution {
    public string RemoveDuplicateLetters(string s) {
        int[] freq = new int[26];
        bool[] used = new bool[26];
        foreach (char c in s) freq[c - 'a']++;
        Stack<char> result = new Stack<char>();
        foreach (char c in s) {
            freq[c - 'a']--;
            if (used[c - 'a']) continue;
            while (result.Count > 0 && result.Peek() > c && freq[result.Peek() - 'a'] > 0) {
                used[result.Pop() - 'a'] = false;
            }
            result.Push(c);
            used[c - 'a'] = true;
        }
        char[] res = result.ToArray();
        Array.Reverse(res);
        return new string(res);
    }

    public static void Main(string[] args) {
        Solution solution = new Solution();
        Console.WriteLine(solution.RemoveDuplicateLetters("cbacdcbc"));
    }
}

This Python code offers a readable solution that retains the resulting direct string construction while handling conditionals effectively. It uses array substitution optimizing continuous consistent checks engaging further space efficiency benefits achieved.

Complexity

Time Complexity: O(n), with n articulating progression sequential handling entire input string.
Space Complexity: O(1), determined constant through compact storage non-collateral deployment.

Try this approach in the editor

Approach 3: Stack

We use an array last to record the last occurrence of each character, a stack to save the result string, and an array vis or an integer variable mask to record whether the current character is in the stack.

Traverse the string s, for each character c, if c is not in the stack, we need to check whether the top element of the stack is greater than c. If it is greater than c and the top element of the stack will appear later, we pop the top element of the stack and push c into the stack.

Finally, concatenate the elements in the stack into a string and return it as the result.

The time complexity is O(n), and the space complexity is O(n). Where n is the length of the string s.

func removeDuplicateLetters(s string) string {
	last := make([]int, 26)
	for i, c := range s {
		last[c-'a'] = i
	}
	stk := []rune{}
	vis := make([]bool, 128)
	for i, c := range s {
		if vis[c] {
			continue
		}
		for len(stk) > 0 && stk[len(stk)-1] > c && last[stk[len(stk)-1]-'a'] > i {
			vis[stk[len(stk)-1]] = false
			stk = stk[:len(stk)-1]
		}
		stk = append(stk, c)
		vis[c] = true
	}
	return string(stk)
}
Try this approach in the editor

Approach 4: Default Approach

func removeDuplicateLetters(s string) string {
	count, in_stack, stack := make([]int, 128), make([]bool, 128), make([]rune, 0)
	for _, c := range s {
		count[c] += 1
	}

	for _, c := range s {
		count[c] -= 1
		if in_stack[c] {
			continue
		}
		for len(stack) > 0 && stack[len(stack)-1] > c && count[stack[len(stack)-1]] > 0 {
			peek := stack[len(stack)-1]
			stack = stack[0 : len(stack)-1]
			in_stack[peek] = false
		}
		stack = append(stack, c)
		in_stack[c] = true
	}
	return string(stack)
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Greedy and Stack

Time Complexity: O(n), where n is the length of the string.
Space Complexity: O(1) as the maximum number of unique characters in a stack is 26.

Approach 2: Iterative with Result String Construction

Time Complexity: O(n), with n articulating progression sequential handling entire input string.
Space Complexity: O(1), determined constant through compact storage non-collateral deployment.

Stack–
Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Greedy + Monotonic StackO(n)O(1)Best general solution. Clear logic for maintaining smallest lexicographic order while ensuring each character appears once.
Iterative Result String ConstructionO(n)O(1)Useful when implementing without an explicit stack structure while applying the same greedy removal logic.

Frequently asked questions

Is Remove Duplicate Letters easy or hard?

Remove Duplicate Letters is typically classified as a medium-level problem. The implementation is not long, but identifying the greedy rule and safely popping characters from a stack requires careful reasoning about future occurrences in the string.

Remove Duplicate Letters Python/Java solution

In Python or Java, implement the greedy stack approach using a list or stack structure along with arrays to track last occurrences and visited characters. Iterate through the string once, pop larger characters that appear later, and append the current character if it hasn't been used yet.

How to solve Remove Duplicate Letters in O(n)?

Record the last index of each character in the string. Iterate through the string and maintain a stack and a visited set. While the stack is not empty and the current character is smaller than the top of the stack and the top appears later again, pop it from the stack. Push the current character if it hasn't been used. This guarantees the lexicographically smallest valid string.

What is the best approach for Remove Duplicate Letters?

The greedy monotonic stack approach is the most efficient solution. Track the last occurrence of each character and iterate through the string once. Maintain a stack and remove larger characters that will appear again later, ensuring the resulting string stays lexicographically smallest. This runs in O(n) time with O(1) extra space.

Is Remove Duplicate Letters asked at Google/Amazon/Meta?

Remove Duplicate Letters frequently appears in interviews at companies like Amazon, Google, and Meta because it tests greedy reasoning and stack usage. Candidates must combine character frequency tracking with a monotonic structure, which is a common pattern in advanced string problems.

What data structure is used in Remove Duplicate Letters?

A stack is the primary data structure used in the optimal solution. The stack maintains the current lexicographically smallest sequence while allowing removal of previously added characters. A hash map or array tracks the last occurrence of each character, and a boolean array or set tracks visited characters.

What is the time complexity of Remove Duplicate Letters?

The optimal solution runs in O(n) time where n is the length of the string. Each character is pushed to and popped from the stack at most once, so the total number of operations is linear. Space complexity is O(1) because the alphabet size is fixed (26 lowercase letters).

Comments

Now write it yourselfClose this page and solve Remove Duplicate Letters in the editor, without looking.
Practise Remove Duplicate Letters