LeetCode ยท #125

Valid Palindrome Solution

Given a string s, return true if it is a palindrome after converting to lowercase and removing all non-alphanumeric characters.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Easy

๐Ÿ”—

LeetCode

Problem #125

๐Ÿ—๏ธ

Pattern

Two Pointers โ€” converge from ends

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

Example 1
Input: s = "A man, a plan, a canal: Panama" Output: true // After cleanup: "amanaplanacanalpanama" โ€” reads same both ways
Example 2
Input: s = "race a car" Output: false // After cleanup: "raceacar" โ€” not a palindrome
Constraints
โ€ข 1 โ‰ค s.length โ‰ค 2 ร— 10โต โ€ข s consists of printable ASCII characters
02
Section Two ยท Approach 1

Brute Force โ€” Build Clean String O(n)

Filter out non-alphanumeric characters, convert to lowercase, then check if the cleaned string equals its reverse. Simple but uses O(n) extra space for the cleaned copy.

Why we can do better
Problem: Building a new string and its reverse requires O(n) extra memory. Two pointers can compare characters in-place from both ends, skipping non-alphanumeric characters on the fly โ€” achieving O(1) space.
Java โ€” Clean + Reverse
class Solution { public boolean isPalindrome(String s) { StringBuilder clean = new StringBuilder(); for (char c : s.toCharArray()) if (Character.isLetterOrDigit(c)) clean.append(Character.toLowerCase(c)); return clean.toString().equals(clean.reverse().toString()); } }
Python โ€” Clean + Reverse
class Solution: def isPalindrome(self, s: str) -> bool: clean = [c.lower() for c in s if c.isalnum()] return clean == clean[::-1]
MetricValue
TimeO(n) โ€” one pass to clean, one to reverse
SpaceO(n) โ€” cleaned string + reversed copy
03
Section Three ยท Approach 2

Two Pointers โ€” O(n) time, O(1) space

Place one pointer at the start and one at the end. Move them inward, skipping non-alphanumeric characters. At each step, compare the two characters (case-insensitive). If they ever differ, return false. If the pointers cross, it's a palindrome.

๐Ÿ’ก Mental model: Two people hold opposite ends of a banner. They each walk inward, ignoring decorations (non-letters). At each step they compare their current letter โ€” if every pair matches, the banner reads the same from both sides.
Algorithm
  • Step 1: Set left = 0, right = s.length() - 1.
  • Step 2: While left < right: skip non-alphanumeric chars by advancing left / retreating right.
  • Step 3: Compare toLowerCase(s[left]) with toLowerCase(s[right]). If different โ†’ false.
  • Step 4: Move both pointers inward: left++, right--.
  • Step 5: If pointers cross without mismatch โ†’ true.
๐ŸŽฏ When to recognize this pattern:
  • Any problem comparing elements from both ends of a sequence โ€” palindrome checks, container problems, sorted-array pair finding.
  • The signal is symmetry: if the answer depends on matching the first with the last, second with second-to-last, etc., use converging two pointers.
04
Section Four ยท Trace

Visual Walkthrough

Trace through s = "A man, a plan, a canal: Panama":

Two Pointers โ€” converge from both ends, skip non-alphanumeric
CLEANED: "amanaplanacanalpanama" (21 chars) a m a n ยทยทยท c ยทยทยท n a m a Lโ†’ โ†R center CONVERGENCE TRACE L=0, R=20: 'a' == 'a' โœ“ L=1, R=19: 'm' == 'm' โœ“ L=2, R=18: 'a' == 'a' โœ“ L=3, R=17: 'n' == 'n' โœ“ ... all pairs continue matching symmetrically ... L=9, R=11: 'a' == 'a' โœ“ L=10, R=10: single middle char 'c' โ€” L โ‰ฅ R โ†’ pointers crossed. DONE! In the original string, pointers skip spaces, commas, and colons automatically. Answer: true โ€” valid palindrome โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Two Pointers
class Solution { public boolean isPalindrome(String s) { int left = 0, right = s.length() - 1; while (left < right) { while (left < right && !Character.isLetterOrDigit(s.charAt(left))) left++; // skip non-alphanumeric while (left < right && !Character.isLetterOrDigit(s.charAt(right))) right--; // skip non-alphanumeric if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) return false; // mismatch left++; right--; } return true; } }
Python โ€” Two Pointers
class Solution: def isPalindrome(self, s: str) -> bool: left, right = 0, len(s) - 1 while left < right: while left < right and not s[left].isalnum(): left += 1 while left < right and not s[right].isalnum(): right -= 1 if s[left].lower() != s[right].lower(): return False left += 1 right -= 1 return True
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
Clean + ReverseO(n)O(n)Simple but builds two extra strings
Two Pointers โ† optimal O(n) O(1) In-place comparison, no extra strings
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
Empty after cleanup" " or ",.!"All non-alphanumeric โ†’ empty string is a palindrome โ†’ true.
Single character"a"Trivially a palindrome.
Mixed case"Aa"Must compare case-insensitively โ†’ true.
Numbers in string"0P"'0' and 'P' are both alphanumeric. '0' โ‰  'p' โ†’ false.
All same character"aaaa"Every pair matches โ†’ true.
โš  Common Mistake: Forgetting the left < right guard inside the inner while-loops that skip non-alphanumeric characters. Without it, pointers can overshoot each other and cause an IndexOutOfBoundsException on strings like ".,,".

โ† Back to Two Pointers problems