1544. Make The String Great - Explanation
Description
Repeatedly remove an adjacent pair when the characters are the same English letter in opposite cases, such as aA or Bb. Continue until no such pair remains and return the resulting string. The result is unique; an empty result is allowed.
Example 1:
Input: s = "a"
Output: "a"Explanation: A single character has no adjacent pair to remove.
Example 2:
Input: s = "abBA"
Output: ""Explanation: First remove the adjacent "bB", leaving "aA". Removing that pair leaves an empty string.
Example 3:
Input: s = "aAbB"
Output: ""Explanation: Remove "aA" and then "bB". Both pairs contain the same letter in opposite cases, so nothing remains.
Constraints:
1 <= s.length <= 100- Only lowercase and uppercase English letters occur.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Stack Data Structure - Used to track and remove adjacent "bad" character pairs efficiently
- ASCII Values - Understanding character codes to detect case differences (uppercase and lowercase differ by 32)
- String Manipulation - Basic operations like character comparison and building result strings
1. Brute Force
Intuition
A "bad" pair consists of the same letter in different cases adjacent to each other (like "aA" or "Aa"). We repeatedly scan the string looking for such pairs. When we find one, we remove both characters and restart the scan since removing a pair might create a new bad pair from previously non-adjacent characters.
Algorithm
- Start from index
0and scan through the string. - At each position
i, check if the current character and the previous character form a bad pair (same letter, different cases). - If a bad pair is found, remove both characters, decrease
iby2to recheck, and update the string length. - Continue until the entire string is scanned without finding any bad pairs.
- Return the resulting string.
Time & Space Complexity
- Time complexity:
- Space complexity:
2. Stack - I
Intuition
A stack naturally handles the "undo" pattern we need. As we process each character, we compare it with the top of the stack. If they form a bad pair (same letter, different cases), we pop the stack, effectively removing both characters. Otherwise, we push the current character. This handles cascading removals automatically.
Algorithm
- Initialize an empty stack to build the result.
- Iterate through each character in the string.
- For each character, check if the stack is non-empty and the top character forms a bad pair with the current character (same letter when lowercased, but different original characters).
- If they form a bad pair, pop the stack.
- Otherwise, push the current character onto the stack.
- Return the stack contents joined as a string.
class Solution:
def makeGood(self, s: str) -> str:
def lower(c):
if ord(c) < ord('a'):
return chr(ord('a') + ord(c) - ord('A'))
return c
stack = []
i = 0
while i < len(s):
if stack and stack[-1] != s[i] and lower(stack[-1]) == lower(s[i]):
stack.pop()
else:
stack.append(s[i])
i += 1
return "".join(stack)Time & Space Complexity
- Time complexity:
- Space complexity:
3. Stack - II
Intuition
We can simplify the bad pair check using ASCII values. In ASCII, the difference between a lowercase letter and its uppercase counterpart is exactly 32 (e.g., 'a' is 97 and 'A' is 65). So if the absolute difference between two characters is 32, they are the same letter in different cases.
Algorithm
- Initialize an empty stack.
- For each character in the string:
- If the stack is non-empty and the absolute ASCII difference between the current character and the stack top is
32, pop the stack. - Otherwise, push the current character.
- If the stack is non-empty and the absolute ASCII difference between the current character and the stack top is
- Return the stack contents as a string.
Time & Space Complexity
- Time complexity:
- Space complexity:
4. Two Pointers
Intuition
Instead of using extra space for a stack, we can simulate it in-place using two pointers. The left pointer l represents the "top" of our virtual stack, while the right pointer r scans through the input. Characters before position l form our result. When we find a bad pair, we "pop" by decrementing l.
Algorithm
- Convert the string to a mutable array and initialize
l = 0. - For each position
rfrom0to the end:- If
l > 0and the character atrforms a bad pair with the character atl - 1(ASCII difference of32), decrementlto "pop" the pair. - Otherwise, copy the character at
rto positionland incrementl.
- If
- Return the substring from index
0tol.
Time & Space Complexity
- Time complexity:
- Space complexity: or depending on the language.
Common Pitfalls
Checking Only for Case Difference Without Same Letter
A "bad" pair requires two conditions: the characters must be the same letter AND have different cases. Checking only s[i] != s[i-1] is insufficient because characters like 'a' and 'B' are different but not a bad pair. You must also verify they are the same letter when case is ignored.
Off-by-One Errors in Index Management
When removing a bad pair and backtracking, the index must be decremented by 2 (not 1) to recheck the newly adjacent characters. Using i -= 1 instead of i -= 2 causes the algorithm to skip potential new bad pairs created by the removal.
Sign in to join the discussion