{x}
blog image

Remove All Adjacent Duplicates In String

You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two adjacent and equal letters and removing them.

We repeatedly make duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique.

 

Example 1:

Input: s = "abbaca"
Output: "ca"
Explanation: 
For example, in "abbaca" we could remove "bb" since the letters are adjacent and equal, and this is the only possible move.  The result of this move is that the string is "aaca", of which only "aa" is possible, so the final string is "ca".

Example 2:

Input: s = "azxxzy"
Output: "ay"

 

Constraints:

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

Solution Explanation

This problem can be efficiently solved using a stack data structure. The idea is to iterate through the input string s. If the current character is the same as the top of the stack, we pop the top element (removing the adjacent duplicates). Otherwise, we push the current character onto the stack. Finally, we join the elements in the stack to form the resulting string.

Time Complexity Analysis

The solution iterates through the input string s once. The push and pop operations on the stack take constant time O(1). Therefore, the overall time complexity is O(n), where n is the length of the input string.

Space Complexity Analysis

The space complexity is determined by the maximum size of the stack. In the worst-case scenario (where no adjacent duplicates exist), the stack will store all the characters from the input string. Therefore, the space complexity is O(n) in the worst case. In the best case (where all characters are duplicates in pairs), the space complexity is O(1).

Code Explanation (Python)

class Solution:
    def removeDuplicates(self, s: str) -> str:
        stk = []  # Initialize an empty list (stack)
        for c in s:  # Iterate through each character in the string
            if stk and stk[-1] == c:  # Check if the stack is not empty and the top element is equal to the current character
                stk.pop()  # Pop the top element (remove duplicate)
            else:
                stk.append(c)  # Push the current character onto the stack
        return ''.join(stk)  # Join the elements in the stack to form the resulting string
 

The Python code directly uses a list as a stack. stk.append(c) adds an element to the end of the list, and stk.pop() removes and returns the last element. stk[-1] accesses the last element without removing it.

Code Explanation (Java)

class Solution {
    public String removeDuplicates(String s) {
        StringBuilder sb = new StringBuilder(); // Use StringBuilder for efficient string manipulation
        for (char c : s.toCharArray()) { // Iterate through each character in the string
            if (sb.length() > 0 && sb.charAt(sb.length() - 1) == c) { //Check if StringBuilder is not empty and last char is equal to current char
                sb.deleteCharAt(sb.length() - 1); //Remove last char (duplicate)
            } else {
                sb.append(c); //Append current char
            }
        }
        return sb.toString(); //Convert StringBuilder to String and return
    }
}

The Java code utilizes a StringBuilder for efficient string manipulations. sb.append(c) adds a character to the end, and sb.deleteCharAt(sb.length() - 1) removes the last character. sb.charAt(sb.length() - 1) accesses the last character.

The other languages' code implementations follow similar logic using their respective stack or string manipulation mechanisms. They all achieve the same time and space complexity.