3561. Resulting String After Adjacent Removals
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.
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 strings
.- If the stack is not empty and the character at the top of the stack and
c
are consecutive (their ASCII values differ by1
or25
, 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.
- If the stack is not empty and the character at the top of the stack and
- 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 EvaluatorExample Walkthrough
Consider the string s = "cabbad"
. Let's walk through how the stack-based solution processes this string:
-
Start with an empty stack:
[]
-
Process
'c'
Stack is empty, so push'c'
: Stack:['c']
-
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']
- Difference is
-
Process
'b'
Check if'b'
and top of stack ('a'
) are consecutive:- Difference is
1
(they are consecutive), so pop'a'
: Stack:['c']
- Difference is
-
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:[]
- Difference is
-
Process
'a'
Stack is empty, so push'a'
: Stack:['a']
-
Process
'd'
Check if'd'
and top of stack ('a'
) are consecutive:- Difference is
3
(not consecutive), so push'd'
: Stack:['a', 'd']
- Difference is
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 strings
is processed exactly once. The stack operations (append
andpop
) each takeO(1)
time, so the total time is linear with respect to the length ofs
. -
Space Complexity:
O(n)
In the worst case, none of the characters are removed, so the stack can contain up ton
characters, wheren
is the length of the input strings
.
Which of the following is a good use case for backtracking?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!