Hello, reader ๐๐ฝ ! Welcome to day 38 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.
Today, I will be picking up LeetCode's daily challenge problem: 1544. Make The String Great.
๐ค Problem Statement
- Given a string s of lower and upper case English letters, return the string after making it good.
A good string is a string which doesn't have two adjacent characters s[i] and s[i + 1] where:
0 <= i <= s.length - 2
s[i]
is a lower-case letter ands[i + 1]
is the same letter but in upper-case or vice-versa.
To make the string good, choose two adjacent characters that make the string bad and remove them. This can be repeatedly done until the string becomes good..
An empty string is also good.
E.g.:
s = "leEeetcode"
=>"leetcode"
s = "abBAcC"
=>""
๐ฌ Thought Process - Stack
- There's a naive approach to solve this problem that takes
O(n^2)
solution, which I shall not be highlighting here. - There's an optimal solution using stack. We traverse the string and keep pushing characters to the stack.
- Before pushing a character, if the top of the stack if it's not empty, we check if the top and the current character differ by an absolute value of 32, then the two characters need to be removed as they make the string bad
- We keep doing this until the end of string.
- Once we traversed the entire string, the stack will hold only those characters that will make the string great.
- We will append these strings and return the new string.
NOTE: Why 32? Because the ASCII value of a is 97 and ASCII value of A is 65. 97 - 65 = 32. Hence difference between all lowercase and uppercase alphabets will be 32.
๐ฉ๐ฝโ๐ป Solution - Stack
class Solution {
public String makeGood(String s) {
if(s == null || s.length() == 0) {
return "";
}
Stack<Character> stack = new Stack<>();
StringBuilder good = new StringBuilder();
int n = s.length();
for(int i = 0; i<n; i++) {
char ch = s.charAt(i);
if(!stack.isEmpty()) {
char top = stack.peek();
if(Math.abs(top - ch) == 32) {
stack.pop();
continue;
}
}
stack.push(ch);
}
for(char ch: stack) {
good.append(ch);
}
return good.toString();
}
}
Time Complexity: O(n)
- n = length of string
Space Complexity: O(n)
- n = length of string
- We use extra space for stack and to build the string.
๐ฌ Thought Process - Two Pointers using StringBuilder
- Another intuitive solution is to use a two pointer sort of solution that keeps checking the current and next character.
- If the two characters differ by
32
, then we ignore these characters and try to match the next character with the previously "good character" - We keep repeatedly doing this until we've removed all bad characters.
- Our good string is supposed to initially begin with the initial string.
- Since we are using StringBuilder, we can do ad-hoc operation and change the string in-place.
๐ฉ๐ฝโ๐ป Solution - Two Pointer using StringBuilder
class Solution {
public String makeGood(String s) {
if(s == null || s.length() <= 0) {
return s;
}
int n = s.length();
int i = 0;
StringBuilder greatString = new StringBuilder(s);
while(i < greatString.length()-1) {
char curr = greatString.charAt(i);
char next = greatString.charAt(i+1);
// If the strings are not same letter but in difference cases
if(curr - 'a' == next - 'A' || curr - 'A' == next - 'a') {
greatString.delete(i, i+2);
if(i > 0) i-=1;
}
else {
i++;
}
}
return greatString.toString();
}
}
Time Complexity: O(N)
N = length of string
Space Complexity: O(N)
N = length of string
- We will have to use additional space to delete bad characters
- You can find the link to the GitHub repo for this question here: 1544. Make The String Great.
Conclusion
That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!
Thank you for reading!