How to Explain Valid Palindrome in a Coding Interview (Step-by-Step)

How to Explain Valid Palindrome in a Coding Interview (Step-by-Step)

Valid Palindrome looks trivial — until the interviewer asks you to handle spaces, punctuation, and mixed case in one clean pass. The two-pointer approach with in-place filtering is what separates a polished answer from a naïve reverse-and-compare.

In this post, you’ll learn how to walk an interviewer through LeetCode 125 with confidence: the cleaning step, the two-pointer technique, the alphanumeric check, and a tight complexity argument.


Problem Statement

LeetCode 125 Easy

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward.

Alphanumeric characters include letters and numbers. Given a string s, return true if it is a palindrome, or false otherwise.

Examples

Example 1
Input: s = “A man, a plan, a canal: Panama”
Output: true
Explanation: “amanaplanacanalpanama” is a palindrome.
Example 2
Input: s = “race a car”
Output: false
Explanation: “raceacar” is not a palindrome.
Example 3
Input: s = ” “
Output: true
Explanation: After removing non-alphanumeric characters, the string is empty — an empty string is a palindrome.

Constraints

  • 1 <= s.length <= 2 * 105
  • s consists only of printable ASCII characters

What Is the Problem?

“I’m given a string that may contain spaces, punctuation, and mixed case. I need to strip everything except letters and digits, lowercase whatever remains, and then check if the result reads the same forwards and backwards. An empty string after cleaning counts as a palindrome.”

Restating the cleaning rules clearly — alphanumeric only, case-insensitive — tells the interviewer you’ve understood the full scope of the problem before touching the keyboard.


Step 1: Compare Approaches Before Jumping to Code

✗ Works but suboptimal
Clean then Reverse
Filter and lowercase into a new string, then compare it to its reverse. Correct, but uses O(n) extra space for two new strings.
✓ Optimal — O(1) space
Two Pointers In-Place
Left and right pointers skip non-alphanumeric characters on the fly and compare directly — no extra strings needed.
“The clean-and-reverse approach works and is fine if the interviewer is happy with O(n) space. But with two pointers we can do the filtering and the comparison in a single pass without allocating any new strings, bringing space down to O(1).”

The Key Insight — Skip, Don’t Strip

💡 Core Insight

Instead of building a cleaned string first, keep two pointers — left at the start and right at the end. Advance each pointer past any non-alphanumeric character, then compare the characters they land on (lowercased). If they ever mismatch, return false. If the pointers cross, every character matched — return true.

“The insight is that I don’t need to materialise a cleaned version of the string. I can treat the two-pointer comparison and the filtering as the same step. Each character is visited at most once by either pointer, so the whole thing is O(n) time and O(1) space.”

Step 2: Explain the Algorithm in Plain English

1 Initialise two pointers

Set left = 0 and right = len(s) - 1. They will march toward each other.

2 Skip non-alphanumeric characters

While left < right: advance left rightward if s[left] is not alphanumeric; advance right leftward if s[right] is not alphanumeric. This skips spaces, punctuation, and symbols without any extra memory.

3 Compare lowercased characters

Once both pointers are on alphanumeric characters, compare s[left].lower() and s[right].lower(). If they differ, return false immediately.

4 Move pointers inward and repeat

After a successful match, do left += 1 and right -= 1, then repeat. If the pointers meet or cross without a mismatch, return true.


Two-Pointer Visualised — “A man, a plan, a canal: Panama”

After cleaning, the effective string is amanaplanacanalpanama. Here’s how the pointers move through the original string, skipping non-alphanumeric characters on the fly.

Step 1
A
m
a
n
,
m
a
A
L=’A’, R=’A’ → match ✓
Step 2
m
a
n
,
m
a
skip space → L=’m’, R=’a’… wait, skip ‘A’ on right → R=’a’ → match ✓
Skip ‘,’
n
n
comma skipped → L=’n’, R=’n’ → match ✓
…continuing
a
n
a
n
a
pointers closing in — all match ✓
Done
a
l
pointers crossed → return true ✓
Left pointer
Right pointer
Matched pair
Skipped (non-alphanumeric or already passed)

Step 3: Talk Through the Code

class Solution:
    def isPalindrome(self, s: str) -> bool:
        left, right = 0, len(s) - 1

        while left < right:
            # Advance left past any non-alphanumeric character
            while left < right and not s[left].isalnum():
                left += 1

            # Advance right past any non-alphanumeric character
            while left < right and not s[right].isalnum():
                right -= 1

            # Both pointers are now on alphanumeric characters
            if s[left].lower() != s[right].lower():
                return False   # Mismatch — not a palindrome

            left  += 1
            right -= 1

        return True  # All characters matched — valid palindrome

Point out the use of Python's built-in .isalnum() — it returns True for both letters and digits, which is exactly the definition given in the problem. Using .lower() at comparison time (rather than pre-processing) keeps the space usage at O(1).


Step 4: Highlight Edge Cases

1 Empty string or all non-alphanumeric
"If the input is ' ' or '!!', both pointers skip every character, the while condition left < right becomes false immediately, and we return true. An empty effective string is defined as a palindrome."
2 Single alphanumeric character
"A single character — even buried in punctuation like '!a!' — is always a palindrome. After skipping, both pointers land on the same character; left never becomes less than right, so we return true without any comparison."
3 Mixed case
"'A' and 'a' must be treated as equal. Calling .lower() on both sides of the comparison handles this without any preprocessing pass over the string."
4 Digits in the string
"Digits count as alphanumeric. So '0P0' is a palindrome — isalnum() returns True for digits, and '0'.lower() == '0' is always true. No special handling needed."
5 String with only one unique character
"'aaaa' or 'a, a' — every comparison matches. The pointers converge without a mismatch and we return true."

Step 5: State Time and Space Complexity

DimensionComplexityReason
Time O(n) Each character is visited at most once by either the left or the right pointer
Space O(1) Only two integer pointers — no new strings or arrays allocated
"Time is O(n) because across the entire run, the left pointer only ever moves right and the right pointer only ever moves left — each character is touched at most once. Space is O(1) because we compare in-place using two indices. The clean-and-reverse approach is also O(n) time but O(n) space — the two-pointer approach is strictly better on memory."

Interview Checklist for Valid Palindrome

  • Restate — "alphanumeric characters only, case-insensitive, check if it reads the same forwards and backwards"
  • Clarify that digits are alphanumeric — confirm with the interviewer if unsure
  • Mention the naïve clean-and-reverse approach first, then explain why two pointers is better
  • State the key insight — skip non-alphanumeric characters in-place rather than stripping them first
  • Use .isalnum() — covers both letters and digits cleanly
  • Use .lower() at comparison time, not as a preprocessing step
  • Guard both inner while loops with left < right to prevent pointer crossing
  • Call out: empty effective string → true
  • Call out: single character → always true
  • State O(n) time and O(1) space — and contrast with the O(n) space alternative

If you can solve Valid Palindrome cleanly, try these next:

💡 Interviewers often follow up Valid Palindrome with "what if you could delete one character?" (LC 680). The answer extends naturally from the two-pointer approach — when a mismatch is found, try skipping the left character or the right character, and check if either sub-range is a palindrome. Knowing the follow-up before it's asked is a strong signal.
Scroll to Top