Facebook Pixel

3561. Resulting String After Adjacent Removals

MediumStackStringSimulation
Leetcode Link

Problem Description

You are given a string s consisting of lowercase English letters.

You must repeatedly perform the following operation while the string s has at least two consecutive characters:

  • Remove the leftmost pair of adjacent characters in the string that are consecutive in the alphabet, in either order (for example, 'a' and 'b', or 'b' and 'a').
  • Shift the remaining characters to the left to fill the gap.

Return the resulting string after no more operations can be performed.

Note: Consider the alphabet as circular, so 'a' and 'z' are also considered consecutive.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To simulate the process of removing pairs of consecutive, adjacent characters, think of scanning the string from left to right. Each time you find a pair, you remove them and the string "collapses." This is similar to handling pairs in a stack: for each character, if it forms a valid pair with the character before it (either next to each other in the alphabet or wrapping around between 'a' and 'z'), you remove the previous character (pop from the stack). If not, you just add the current character (push onto the stack). Using a stack helps efficiently keep track of which characters could possibly pair and be removed as you move through the string.

Solution Approach

A stack is used to efficiently simulate the process of removing adjacent consecutive letters. Here is how the algorithm works:

  • Start with an empty stack.
  • Iterate through each character c in the string s.
    • If the stack is not empty and the character at the top of the stack and c are consecutive (their ASCII values differ by 1 or 25, which covers the wrap-around between 'a' and 'z'), pop the top character from the stack. This removes the leftmost adjacent consecutive pair.
    • Otherwise, push the current character c onto the stack.
  • After processing all characters, the stack contains the remaining characters in their original order which could not be removed.
  • Combine the stack into a string and return it.

In code, this process looks like:

stk = []
for c in s:
    if stk and abs(ord(c) - ord(stk[-1])) in (1, 25):
        stk.pop()
    else:
        stk.append(c)
return "".join(stk)

This approach works efficiently in a single pass through the string, and the stack structure naturally handles the removal and shifting as required by the problem statement.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Consider the string s = "cabbad". Let's walk through how the stack-based solution processes this string:

  1. Start with an empty stack: []

  2. Process 'c' Stack is empty, so push 'c': Stack: ['c']

  3. Process 'a' Check if 'a' and top of stack ('c') are consecutive:

    • Difference is abs(ord('a') - ord('c')) = 2 (not consecutive), so push 'a': Stack: ['c', 'a']
  4. Process 'b' Check if 'b' and top of stack ('a') are consecutive:

    • Difference is 1 (they are consecutive), so pop 'a': Stack: ['c']
  5. Process 'b' (the next 'b') Check if 'b' and top of stack ('c') are consecutive:

    • Difference is 1 (they are consecutive), so pop 'c': Stack: []
  6. Process 'a' Stack is empty, so push 'a': Stack: ['a']

  7. Process 'd' Check if 'd' and top of stack ('a') are consecutive:

    • Difference is 3 (not consecutive), so push 'd': Stack: ['a', 'd']

Result: The stack now contains ['a', 'd']. So, after all removals, the resulting string is "ad".

This example shows how each adjacent pair of consecutive letters gets removed, including the effect of "collapsing" the string as removals happen.

Solution Implementation

1class Solution:
2    def resultingString(self, s: str) -> str:
3        # Initialize an empty stack to store result characters
4        stack = []
5        for char in s:
6            # If stack is not empty and the absolute difference between the ASCII values
7            # of the current character and the character at the top of the stack is 1 or 25
8            # (which means they are adjacent in the alphabet, or wrap from 'a' to 'z'/'z' to 'a')
9            if stack and abs(ord(char) - ord(stack[-1])) in (1, 25):
10                # Remove the top character from the stack
11                stack.pop()
12            else:
13                # Otherwise, push the current character onto the stack
14                stack.append(char)
15        # Join all characters in the stack to form the resultant string
16        return "".join(stack)
17
1class Solution {
2    public String resultingString(String s) {
3        // Use StringBuilder as a stack to build the resulting string
4        StringBuilder stack = new StringBuilder();
5        for (char currentChar : s.toCharArray()) {
6            // Check if stack is not empty and the top character is contiguous with the current character
7            if (stack.length() > 0 && isContiguous(stack.charAt(stack.length() - 1), currentChar)) {
8                // Remove the top character if contiguous
9                stack.deleteCharAt(stack.length() - 1);
10            } else {
11                // Otherwise, add the current character to the stack
12                stack.append(currentChar);
13            }
14        }
15        // Return the final constructed string
16        return stack.toString();
17    }
18
19    // Check if characters a and b are contiguous in the alphabet (cyclic, e.g., 'a' and 'z')
20    private boolean isContiguous(char a, char b) {
21        int diff = Math.abs(a - b);
22        // 'a' and 'b', ... 'y' and 'z', and also 'a' and 'z' (cyclic: 25)
23        return diff == 1 || diff == 25;
24    }
25}
26
1class Solution {
2public:
3    // Function that processes the string by removing adjacent characters
4    // where their ASCII codes differ by 1 or 25 (for 'a' and 'z').
5    string resultingString(string s) {
6        string stack; // Use a string as a stack to store the processed characters
7
8        for (char ch : s) {
9            // Check if stack is not empty and the top character differs by 1 or 25
10            if (!stack.empty() &&
11                (abs(stack.back() - ch) == 1 || abs(stack.back() - ch) == 25)) {
12                stack.pop_back(); // Remove the last character if adjacent rule satisfied
13            } else {
14                stack.push_back(ch); // Otherwise, add current character to stack
15            }
16        }
17
18        return stack; // The resultant string after all removals
19    }
20};
21
1// Helper function: returns true if characters a and b are contiguous in the alphabet (e.g., 'a' and 'b', or 'z' and 'a')
2function isContiguous(a: string, b: string): boolean {
3    const diff = Math.abs(a.charCodeAt(0) - b.charCodeAt(0));
4    // Char codes are contiguous if their difference is 1 (e.g. 'a' and 'b'), or 25 (e.g. 'a' and 'z')
5    return diff === 1 || diff === 25;
6}
7
8// Main function: processes the string and removes contiguous character pairs
9function resultingString(s: string): string {
10    const stack: string[] = [];
11
12    // Iterate through each character in the input string
13    for (const ch of s) {
14        // If the stack is not empty and the top element is contiguous with the current character
15        if (stack.length > 0 && isContiguous(stack[stack.length - 1], ch)) {
16            stack.pop(); // Remove the top element from the stack
17        } else {
18            stack.push(ch); // Otherwise, add current character to the stack
19        }
20    }
21
22    // Join all remaining characters to form the resulting string
23    return stack.join('');
24}
25

Time and Space Complexity

  • Time Complexity: O(n) Each character in the string s is processed exactly once. The stack operations (append and pop) each take O(1) time, so the total time is linear with respect to the length of s.

  • Space Complexity: O(n) In the worst case, none of the characters are removed, so the stack can contain up to n characters, where n is the length of the input string s.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Which of the following is a good use case for backtracking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More