Limiting duplicate character occurrence to once in JavaScript

In JavaScript, removing duplicate characters while maintaining lexicographical order requires a strategic approach. This problem asks us to keep only one occurrence of each character, choosing positions that result in the lexicographically smallest string.

Problem Statement

We need to write a JavaScript function that takes a string and returns a new string where each character appears only once. The key constraint is that the result must be lexicographically smallest among all possible combinations.

For example, with input 'cbacdcbc', the output should be 'acdb'.

Understanding the Algorithm

The solution uses a greedy approach:

  • Find the rightmost position of each character
  • Build the result by selecting characters that maintain lexicographical order
  • Ensure all unique characters are included

Complete Solution

const str = 'cbacdcbc';

const removeDuplicates = (str = '') => {
    if (str.length  0 && 
               result[result.length - 1] > char && 
               count[result[result.length - 1]] > 0) {
            const removed = result.pop();
            inResult.delete(removed);
        }
        
        result.push(char);
        inResult.add(char);
    }
    
    return result.join('');
};

console.log(removeDuplicates(str));
console.log(removeDuplicates('bcabc')); // "abc"
console.log(removeDuplicates('cbacdcbc')); // "acdb"
acdb
abc
acdb

How It Works

The algorithm maintains a stack-based approach:

  1. Count frequencies: Track how many times each character appears
  2. Process each character: Decrease its count and check if it's already included
  3. Remove larger characters: Pop characters from result if they're larger than current character and appear again later
  4. Add current character: Push to result and mark as included

Step-by-Step Example

For input 'cbacdcbc':

// Demonstrating the process step by step
const debugRemoveDuplicates = (str) => {
    const count = {};
    for (let char of str) {
        count[char] = (count[char] || 0) + 1;
    }
    
    console.log('Initial counts:', count);
    
    const result = [];
    const inResult = new Set();
    
    for (let i = 0; i  0 && 
               result[result.length - 1] > char && 
               count[result[result.length - 1]] > 0) {
            const removed = result.pop();
            inResult.delete(removed);
            console.log(`Removed '${removed}' from result`);
        }
        
        result.push(char);
        inResult.add(char);
        console.log(`Added '${char}' to result:`, result.join(''));
        console.log('---');
    }
    
    return result.join('');
};

debugRemoveDuplicates('cbacdcbc');
Initial counts: { c: 4, b: 2, a: 1, d: 1 }
Processing 'c' at index 0
Remaining count: 3
Added 'c' to result: c
---
Processing 'b' at index 1
Remaining count: 1
Added 'b' to result: cb
---
Processing 'a' at index 2
Remaining count: 0
Removed 'b' from result
Removed 'c' from result
Added 'a' to result: a
---
Processing 'c' at index 3
Remaining count: 2
Added 'c' to result: ac
---
Processing 'd' at index 4
Remaining count: 0
Added 'd' to result: acd
---
Processing 'c' at index 5
Remaining count: 1
'c' already in result, skipping
Processing 'b' at index 6
Remaining count: 0
Added 'b' to result: acdb
---
Processing 'c' at index 7
Remaining count: 0
'c' already in result, skipping
acdb

Time and Space Complexity

Aspect Complexity Explanation
Time O(n) Each character is pushed and popped at most once
Space O(1) Fixed space for 26 lowercase letters

Conclusion

This greedy stack-based algorithm efficiently removes duplicate characters while maintaining lexicographical order. The key insight is using character frequency to decide when it's safe to remove characters from the result.

Updated on: 2026-03-15T23:19:00+05:30

227 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements