Remove Palindromic Subsequences
EasyPrompt
You are given a string s consisting only of letters 'a' and 'b'. In a single step you can remove one palindromic subsequence from s.
Return the minimum number of steps to make the given string empty.
A string is a subsequence of a given string if it is generated by deleting some characters of a given string without changing its order. Note that a subsequence does not necessarily need to be contiguous.
A string is called palindrome if is one that reads the same backward as well as forward.
Example 1:
Input: s = "ababa"
Output: 1
Explanation: s is already a palindrome, so its entirety can be removed in a single step.Example 2:
Input: s = "abb"
Output: 2
Explanation: "abb" -> "bb" -> "".
Remove palindromic subsequence "a" then "bb".Example 3:
Input: s = "baabb"
Output: 2
Explanation: "baabb" -> "b" -> "".
Remove palindromic subsequence "baab" then "b".
Constraints:
1 <= s.length <= 1000s[i]is either'a'or'b'.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach hinges on the key observation that the problem can be simplified to determining if the input string is a palindrome. The string s consists only of 'a's and 'b's.
- If
sis already a palindrome, it can be removed in one step. - If
sis not a palindrome, we can always remove all occurrences of 'a' as one palindromic subsequence, and then all occurrences of 'b' as a second palindromic subsequence. This is because a sequence of identical characters (like "aaaa" or "bbb") is always a palindrome. Therefore, any non-palindromic string can be cleared in exactly two steps.
This approach checks if the string is a palindrome by creating a reversed copy of the string and comparing it to the original.
Algorithm
- Check if the input string
sis empty. If so, return 0. - Create a new string by reversing
s. - Compare the original string
swith the reversed string. - If they are identical,
sis a palindrome. Return 1. - Otherwise,
sis not a palindrome. Return 2.
Walkthrough
The algorithm first handles the edge case of an empty string, which requires 0 steps. For non-empty strings, it creates a new string that is the reverse of the input string s. It then compares this new reversed string with the original string s. If they are equal, it means s is a palindrome, and the answer is 1. If they are not equal, s is not a palindrome, and based on our core logic, the answer is 2.
class Solution { public int removePalindromeSub(String s) { if (s.isEmpty()) { return 0; } String reversedS = new StringBuilder(s).reverse().toString(); if (s.equals(reversedS)) { return 1; } else { return 2; } }}Complexity
Time
O(N), where N is the length of the string. Creating the reversed string using `StringBuilder` takes O(N) time, and comparing the two strings also takes O(N) time.
Space
O(N), as it requires extra space to store the reversed copy of the string.
Trade-offs
Pros
Simple to understand and implement using built-in string manipulation functions.
Cons
Uses extra space that is proportional to the input string's length, which is less efficient than an in-place check.
This is the most efficient approach. It uses the same core logic as the previous method: the answer is 1 if the string is a palindrome, and 2 otherwise. The improvement comes from a more efficient way to check for a palindrome. Instead of creating a new reversed string, this method uses two pointers to check for palindromic properties in-place, which optimizes space usage.
Algorithm
- Handle the base case: if the string
sis empty, return 0. - Initialize two pointers:
leftat the beginning of the string (index 0) andrightat the end (indexs.length() - 1). - Iterate with a
whileloop as long asleftis less thanright. - Inside the loop, compare the characters at the
leftandrightpointers. - If
s.charAt(left)is not equal tos.charAt(right), the string is not a palindrome. Return 2. - If the characters match, move the pointers closer to the center: increment
leftand decrementright. - If the loop finishes, the string is a palindrome. Return 1.
Walkthrough
The algorithm starts by handling the base case of an empty string. For non-empty strings, it initializes two pointers: left at the start of the string (index 0) and right at the end (index s.length() - 1). It then iterates while left is less than right, comparing the characters at these two pointers. If at any point the characters do not match, the string is not a palindrome, and we can immediately return 2. If the characters match, the pointers are moved inwards. If the loop completes without finding any mismatches, the string is a palindrome, and we return 1.
class Solution { public int removePalindromeSub(String s) { if (s.isEmpty()) { return 0; } int left = 0; int right = s.length() - 1; while (left < right) { if (s.charAt(left) != s.charAt(right)) { return 2; // Not a palindrome } left++; right--; } return 1; // Is a palindrome }}Complexity
Time
O(N), where N is the length of the string. In the worst case (the string is a palindrome), the two pointers traverse half of the string, resulting in N/2 comparisons.
Space
O(1), as it only uses a constant amount of extra space for the two pointer variables, regardless of the input size.
Trade-offs
Pros
Highly efficient in terms of space complexity.
Often faster in practice for non-palindromes as it can exit early.
Cons
This is the optimal solution for this problem, so there are no significant cons.
Solutions
Solution
class Solution {public int removePalindromeSub(String s) { for (int i = 0, j = s.length() - 1; i < j; ++i, --j) { if (s.charAt(i) != s.charAt(j)) { return 2; } } return 1; }}class Solution {public: int removePalindromeSub(string s) { for (int i = 0, j = s.size() - 1; i < j; ++i, --j) { if (s[i] != s[j]) { return 2; } } return 1; }};class Solution: def removePalindromeSub( self, s: str) -> int: return 1 if s[:: - 1] == s else 2Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.