Example 1
ready- Input
s = "A man, a plan, a canal: Panama"- Expected
true- Why
- letters/digits only, case-insensitive
A string is a palindrome when it reads the same forwards and backwards — but here we judge only the letters and digits, and we don't care about case.
Given a string s, return true if it's a palindrome under those rules, and false
otherwise.
Concretely, before comparing you should treat the string as if you had:
A string that ends up empty after step 1 counts as a palindrome.
1 <= s.length <= 200_000s consists of printable ASCII characters (letters, digits, punctuation, spaces).Hints
Build the cleaned string, then compare it to its reverse. Two passes, some extra memory — perfectly valid, and worth writing first.
Put one finger on each end. If both point at alphanumeric characters, compare them (lowercased) and step both inward. If either points at junk, step just that one. You never build a second string at all.
When you skip non-alphanumerics inside the loop, make sure the two pointers can't
run past each other — left < right needs re-checking inside the skip loops too.
Your language probably has a one-liner for this. The converging-pointers version is still worth owning: it's the same movement pattern you'll use on Two Sum II, 3Sum, and Container With Most Water — where no one-liner will save you.
Visible cases
s = "A man, a plan, a canal: Panama"trues = "race a car"falses = " "trueInterview signal
Do exactly what the statement says: filter to alphanumerics, lowercase, compare with the reversal.
def is_palindrome(s: str) -> bool:
cleaned = [c.lower() for c in s if c.isalnum()]
return cleaned == cleaned[::-1]Time O(n), space O(n) for the cleaned copy. Honest and readable — if an interviewer asks for better, they mean the space.
Skip the copy. Walk left from the front and right from the back; each step, slide
past non-alphanumerics, then compare the two survivors case-insensitively.
def is_palindrome(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 TrueThe only subtlety is the guard left < right inside both skip-loops: a string like
".," would otherwise let the pointers cross while skipping and read out of bounds
(or compare junk). With the guard, all-punctuation strings fall straight through to
True — matching the "empty counts as palindrome" rule.
Time O(n) — each pointer moves at most n steps total. Space O(1) — this is the pattern's whole sales pitch: the answer emerges from positions, not from a rebuilt copy.
Ready to run.
3 cases are queued.
Visible testcase
Case 1
Expected
true