The "Make The String Great" problem requires you to process a string s
such that you remove adjacent pairs of characters where one is a lowercase letter and the other is the same letter in uppercase (or vice versa). Specifically, for any two adjacent characters s[i]
and s[i+1]
, if s[i]
and s[i+1]
are the same letter but with different cases (e.g., 'a' and 'A'), both should be removed. This process is repeated until no such adjacent pairs remain. The final string, after all possible removals, is returned. If the resulting string is empty, return an empty string ""
.
At first glance, the problem seems to require checking for adjacent pairs and removing them, repeating the process until the string stabilizes. A brute-force approach would involve scanning the string repeatedly and removing pairs as they are found, but this would be inefficient for longer strings, as each removal could require a new scan from the start.
To optimize, we can think of using a stack data structure. The stack helps us efficiently compare the current character with the last character added. If they form a "bad" pair (same letter, different case), we pop the last character from the stack (removing both). Otherwise, we push the current character onto the stack. This way, we only need to scan the string once, and the stack always contains the current "good" string.
This is analogous to checking for balanced parentheses or removing duplicates, where the stack naturally handles undoing previous steps if a new conflict arises.
s
.
Why use a stack? Because it allows us to efficiently undo previous choices if a new adjacent pair forms, without having to rescan the string.
Sample Input: s = "leEeetcode"
No more adjacent "bad" pairs remain. Final output: "leetcode"
n
, this could result in O(n2) time complexity.The "Make The String Great" problem is elegantly solved using a stack to efficiently process and remove adjacent letter pairs that differ only in case. This approach avoids repeated rescanning and leverages the stack's properties to handle cascading removals. The final solution is both simple and efficient, with linear time and space complexity, and ensures only valid adjacent pairs are removed until the string is "great".
class Solution:
def makeGood(self, s: str) -> str:
stack = []
for c in s:
if stack and abs(ord(stack[-1]) - ord(c)) == 32:
stack.pop()
else:
stack.append(c)
return ''.join(stack)
class Solution {
public:
string makeGood(string s) {
string stack = "";
for (char c : s) {
if (!stack.empty() && abs(stack.back() - c) == 32) {
stack.pop_back();
} else {
stack.push_back(c);
}
}
return stack;
}
};
class Solution {
public String makeGood(String s) {
StringBuilder stack = new StringBuilder();
for (char c : s.toCharArray()) {
int len = stack.length();
if (len > 0 && Math.abs(stack.charAt(len - 1) - c) == 32) {
stack.deleteCharAt(len - 1);
} else {
stack.append(c);
}
}
return stack.toString();
}
}
var makeGood = function(s) {
let stack = [];
for (let c of s) {
if (stack.length > 0 && Math.abs(stack[stack.length - 1].charCodeAt(0) - c.charCodeAt(0)) === 32) {
stack.pop();
} else {
stack.push(c);
}
}
return stack.join('');
};