Valid Palindrome
Converge from both ends, skipping non-alphanumerics.
Approach
Put one pointer at each end. Advance past any non-alphanumeric character, compare the two (case-insensitively), and move inward. If any pair differs it's not a palindrome. This avoids building a cleaned copy of the string.
Time complexity
O(n)
Space complexity
O(1)
Common mistake
Forgetting to skip punctuation/spaces on both pointers, or comparing without normalizing case.
Interactive walkthrough — step through it below, free and without an account
Problem
Given a string, decide whether its meaningful characters form a palindrome. Ignore punctuation, spaces, and other non-alphanumeric characters, and treat uppercase and lowercase letters as equivalent. Return true when the normalized sequence reads identically from both directions.
Examples
Input: "A man, a plan, a canal: Panama"
Output: true
Input: "race a car"
Output: false
Problem
Problem
Given a string, decide whether its meaningful characters form a palindrome. Ignore punctuation, spaces, and other non-alphanumeric characters, and treat uppercase and lowercase letters as equivalent. Return true when the normalized sequence reads identically from both directions.
Problem and Goal
Learning goal: Recognize what counts as a matching character pair in a valid palindrome.
Core idea The original string may contain spaces, punctuation, and mixed capitalization, but those characters should not affect the answer. Conceptually, we normalize the string by retaining only letters and digits and converting letters to one case. We will avoid physically creating that normalized copy by applying the same rules while scanning.
Algorithm plan
- Start with one index at the left end and one at the right end.
- Ignore every non-alphanumeric character encountered by either index.
- Compare the remaining characters case-insensitively.
- Stop with false on the first mismatch; otherwise continue until the pointers meet.
Micro-example
For "A, b", the comma and space disappear conceptually, leaving "ab".
The normalized characters differ, so the result is false.
Pitfalls
- Do not let punctuation participate in a comparison.
- Normalize case before comparing letters.
- Check that each pointer remains within the string while skipping.
Recap
- Only alphanumeric characters matter.
- Letter case does not matter.
- The answer depends on mirrored pairs.
Problem and Goal
Normalize conceptually, not physically
Compare only letters and digits, treating uppercase and lowercase as equal.
The target sequence is a filtered, case-insensitive view of the original string.
Key Idea: Converge from Both Ends
Learning goal: Understand why two pointers can verify every relevant mirrored pair without building a cleaned string.
Core idea
A palindrome has equal characters at symmetric positions: the first matches the last, the second matches the second-to-last, and so on. Place left at the beginning and right at the end, then move them inward after each valid comparison. If either side points at punctuation, advance that pointer until it reaches an alphanumeric character.
Key insight
Skipping characters changes where we compare, but it never changes the order of the meaningful characters.
Algorithm plan
- While
left < right, skip non-alphanumerics from the left. - Skip non-alphanumerics from the right.
- Compare lowercase versions of
s[left]ands[right]. - Move both pointers inward after a match.
Micro-example
In "a!b", left sees a and right sees b; they differ immediately.
In "a!a", the right pointer skips ! and compares a with a.
Pitfalls
- Skipping only on one side can compare unrelated characters.
- Moving a pointer before comparison can skip a meaningful character.
- The loop should stop when the pointers cross or meet.
Recap
- Each comparison checks one mirrored pair.
- Skipping is performed independently on both ends.
- Matching pairs let both pointers move inward.
Key Idea: Converge from Both Ends
Advance a pointer while its character is not alphanumeric.
Move past commas, spaces, and colons.
Main Approach and Correctness
Learning goal: Turn the two-pointer idea into a precise linear-time algorithm.
Core idea At every iteration, the pointers identify the next unverified meaningful characters from opposite ends. If they match after case normalization, that pair agrees with the palindrome requirement, so both pointers can safely move inward. A mismatch proves the whole string is not a palindrome because no ignored character can repair a mismatch between meaningful characters.
Algorithm plan
- Set
left = 0andright = len(s) - 1. - While
left < right, skip non-alphanumeric characters on both sides. - Compare
lower(s[left])withlower(s[right]). - Return false if they differ; otherwise increment
leftand decrementright. - Return true when the pointers meet or cross.
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, right = left + 1, right - 1
return TrueMicro-example
For "No lemon, no melon", the outer N and n match after normalization.
The pointers continue until every meaningful pair has been checked.
Pitfalls
- Use bounds in both skip loops.
- Normalize only for comparison; the original string need not change.
- A single mismatch is enough to return false.
Recap
- The invariant covers all settled outer pairs.
- Every character is skipped or compared at most once.
- No cleaned copy is required.
Main Approach and Correctness
| Approach | Time | Extra space | Tradeoff |
|---|---|---|---|
| Clean, reverse, compare | O(n) | O(n) | Simple, but stores a normalized copy |
| Two pointers | O(n) | O(1) | Scans in place while skipping ignored characters |
Walkthrough: One Example End to End
Learning goal: Trace pointer movement on a string containing spaces, punctuation, and mixed case.
Core idea
We will trace "A man, a plan, a canal: Panama", the first example. The pointers do not rewrite the input. Instead, each pointer independently skips characters that are irrelevant, then the algorithm compares the next meaningful pair.
Algorithm plan
- Begin at indices
0and29. - Skip the space or punctuation whenever a pointer lands on it.
- Compare pairs such as
Awitha, thenmwithm. - Continue until the pointers meet at the central
a.
Micro-example
The first comparison is A versus a, which matches after lowercasing.
Near the middle, punctuation is skipped before the final meaningful pairs are checked.
Pitfalls
- The colon at index
21must be skipped rather than compared. - A space is not the counterpart of a letter.
- Case normalization must happen for every comparison, not just the first.
Recap
- The walkthrough checks the exact example from the problem statement.
- Pointer skips and comparisons are separate actions.
- Every successful comparison shrinks the unresolved range.
Walkthrough: One Example End to End
Animate both pointers across the concrete palindrome example.
Pitfalls and Edge Cases
Learning goal: Prevent the indexing and normalization mistakes that commonly break this solution.
Core idea The algorithm has two independent preprocessing actions: each pointer skips its own ignored characters, and the comparison normalizes both remaining characters. Omitting either action changes the problem being solved. Empty meaningful content, a single character, and strings with punctuation at the boundaries should all be handled naturally.
Algorithm plan
- Guard every skip with a valid index condition.
- Skip from the left and right before reading the comparison characters.
- Compare lowercase forms, not raw characters.
- Treat reaching the middle as success unless a mismatch was found.
Micro-example
For ".,A!", the right pointer skips !, the left pointer skips ., and A is the only meaningful character.
Since no pair disagrees, the answer is true.
Pitfalls
- Explicit mistake: Forgetting to skip punctuation or spaces on both pointers can compare a letter with irrelevant text.
- Explicit mistake: Comparing without normalizing case makes
Aandaincorrectly appear different. - Do not assume the first and last raw characters are meaningful.
- Do not allocate a filtered string if constant space is required.
Recap
- Skip independently on both sides.
- Normalize every letter comparison.
- Boundary-heavy inputs are valid test cases.
Pitfalls and Edge Cases
What must happen before comparing the current characters?
Complexity and Wrap-up
Learning goal: Justify the efficiency of the converging-pointer solution and summarize when to use it.
Core idea Although there are nested-looking skip loops, each pointer only moves forward through the string once. A character is either skipped or included in one comparison, so the total work is linear rather than quadratic. The algorithm stores only a few indices and temporary values, meeting the constant-space target.
Algorithm plan
- Scan inward from both ends.
- Charge each pointer movement to the character it passes.
- Stop immediately on a mismatch.
- Otherwise return true after the unresolved range closes.
Micro-example
For an input of length n, the pointers together make at most n forward moves.
The variables left, right, and temporary characters use constant extra memory.
Pitfalls
- Do not describe the skip loops as
O(n^2); pointers never move backward. - Building a normalized copy would use
O(n)space and miss the target optimization. - Early termination improves practical time but does not change the worst-case bound.
Recap
- Time complexity:
O(n). - Extra space:
O(1). - The invariant plus monotonic pointer movement gives both correctness and efficiency.
Complexity and Wrap-up
Show the asymptotic cost and why the two-pointer scan meets the target.
Now try it on your own problem
Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.
Build a lesson from any problem
