Skip to content
← All posts

Remove Palindromic Subsequences: The Tricky Two-Letter Insight

4 min read
leetcodeproblemeasystrings

You are given a string s consisting only of letters 'a' and 'b'. In one step, you can remove any palindromic subsequence from s. Return the minimum number of steps to make the string empty.

This is LeetCode 1332: Remove Palindromic Subsequences.

01234ababaoriginalRemoval 1: all "a"saaa"aaa" is a palindromeRemoval 2: all "b"sbb"bb" is a palindrome
For "ababa", remove all 'a's as one palindromic subsequence, then all 'b's as another. Two removals.

Why this problem matters

This problem trips up a lot of people because they confuse subsequence with substring. A substring is a contiguous block of characters. A subsequence can pick characters from anywhere in the string, as long as they stay in relative order. That distinction changes everything here. If the problem asked you to remove palindromic substrings, it would be much harder. But with subsequences, you have far more freedom.

The trick is recognizing what that freedom gives you. Since the string only contains two distinct characters, 'a' and 'b', you can always gather all the 'a's into one subsequence and all the 'b's into another. A string made of a single repeated character, like "aaa" or "bb", is always a palindrome. That means you never need more than two removals.

This is a great example of a problem where reading carefully matters more than writing clever code. The solution is almost trivial once you understand the constraint, but if you miss the distinction between subsequence and substring, you might spend a long time building something far more complicated than necessary.

The key insight

Since s only contains 'a' and 'b', think about what subsequences you can form. All the 'a' characters in the string, taken in order, form the subsequence "aaa...a". That is a palindrome. Likewise, all the 'b' characters form "bbb...b", also a palindrome. So you can always empty the string in at most two steps: first remove all the 'a's, then remove all the 'b's.

Can you do better? Only in two special cases. If the string is already empty, you need zero removals. If the entire string is already a palindrome, you can remove it all in one step (the whole string is itself a palindromic subsequence). Otherwise, the answer is always 2.

That gives you a clean three-way check: empty means 0, palindrome means 1, everything else means 2.

The solution

def removePalindromeSubsequences(s: str) -> int:
    if not s:
        return 0
    if s == s[::-1]:
        return 1
    return 2

The function checks whether the string is empty first. If so, it returns 0. Then it checks whether the string equals its reverse, which is the standard palindrome test. If the string is a palindrome, you can remove the entire thing in one step, so return 1. In all other cases, you need exactly two steps: one to remove all the 'a's and one to remove all the 'b's.

Remember the difference between subsequence and substring. A subsequence can skip characters, so "aba" is a subsequence of "aabba" (pick indices 0, 2, 4). A substring must be contiguous, so "aba" is not a substring of "aabba". This problem only works because subsequences give you the flexibility to gather all characters of the same type.

Visual walkthrough

Case 1: The string is empty

empty string

If the string is "", there is nothing to remove. Return 0.

Case 2: The string is already a palindrome

01234abbbapalindromes == reverse(s) → answer = 1

If s equals its reverse, it is one palindromic subsequence by itself. Return 1.

Case 3: The string is not a palindrome

Original:ababaStep 1: remove "aaa"bbremainingStep 2: remove "bb"empty

Remove all "a"s in one step (they form "aaa...a", always a palindrome), then all "b"s in another. Return 2.

The three cases cover every possible input. Empty strings need zero work. Palindromes can be removed whole. Everything else splits into exactly two palindromic subsequences, one for each letter.

Complexity analysis

ApproachTimeSpace
Palindrome checkO(n)O(1)

The time complexity is O(n) because comparing a string to its reverse requires checking each character once. In Python, s[::-1] creates a reversed copy in O(n) time. The space complexity depends on the language. In Python, the slice creates an O(n) copy, but you could implement the palindrome check with two pointers and O(1) extra space. Either way, the algorithm is linear time.

The building blocks

1. Palindrome check

The core operation is testing whether a string reads the same forwards and backwards. You can do this by comparing s to s[::-1], or by using two pointers starting from the ends and moving inward. Both approaches take O(n) time. This same check appears in many palindrome problems, so it is worth drilling until it is automatic.

2. Subsequence vs substring

Understanding the distinction between subsequences and substrings is essential for dozens of LeetCode problems. A subsequence preserves relative order but can skip elements. A substring must be contiguous. Here, the subsequence property lets you collect all 'a's (or all 'b's) regardless of where they appear in the string. If this were a substring problem, you could not do that.

Edge cases

  • Empty string "". No characters to remove. Return 0.
  • Single character like "a". It is a palindrome. Return 1.
  • Already a palindrome like "abba" or "aba". Remove the whole string in one step. Return 1.
  • All same characters like "aaaa". This is a palindrome. Return 1.
  • Not a palindrome like "ab" or "aab". Remove all 'a's first, then all 'b's. Return 2.

From understanding to recall

The logic is simple, but under interview pressure you might second-guess yourself. "Is the answer really always 0, 1, or 2? Am I missing something?" Confidence comes from having worked through the reasoning multiple times. Spaced repetition helps you internalize both the palindrome check and the key insight about two-character alphabets, so you recall the solution instantly instead of rederiving it.

Related posts

CodeBricks breaks this problem into its palindrome check and subsequence reasoning building blocks, then drills them independently with spaced repetition. You type each piece from scratch until it becomes automatic. When a string problem shows up in your interview, you recognize the pattern and write it without hesitation.