032. Normalized Palindrome
Problem
For an ASCII string, ignore non-alphanumeric characters and letter case, then decide whether the remaining sequence is a palindrome. Return True when no alphanumeric characters remain. Do not allocate a filtered copy.
Examples
Example 1
normalized_palindrome("A man, a plan, a canal: Panama")
=> TrueIgnoring spaces, punctuation and case leaves matching endpoints.
Example 2
normalized_palindrome("!!!")
=> TrueThe meaningful sequence is empty.
Example 3
normalized_palindrome("race a car")
=> FalseA meaningful endpoint pair eventually differs.
Reveal the worked solution and reasoning
Why this mechanism works
Opposite ends can be compared after irrelevant characters are skipped independently. A mismatch at the next meaningful pair disproves a palindrome immediately. The skip loops need bounds checks because the entire remaining region may be punctuation.
State and boundaries
left and right delimit unexamined characters. All meaningful pairs outside them have already matched.
Protocol
- Start at both ends.
- Skip non-alphanumeric characters on the left.
- Skip them on the right.
- Compare lowercase meaningful characters.
- Reject a mismatch or move both pointers inward.
Visualization
A man, a plan, a canal: Panama
A <-----------------------> a
next meaningful pair: m <-> mPython implementation
def normalized_palindrome(text):
left, right = 0, len(text) - 1
while left < right:
while left < right and not text[left].isalnum():
left += 1
while left < right and not text[right].isalnum():
right -= 1
if text[left].lower() != text[right].lower():
return False
left += 1
right -= 1
return True
Worked execution
normalized_palindrome("A man, a plan, a canal: Panama")
L2 normalized_palindrome: left, right = 0, len(text) - 1 text='A man, a plan, a canal: Panama'; left=0; right=29 L10 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=1; right=29 L11 normalized_palindrome: right -= 1 text='A man, a plan, a canal: Panama'; left=1; right=28 L5 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=2; right=28 L10 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=3; right=28 L11 normalized_palindrome: right -= 1 text='A man, a plan, a canal: Panama'; left=3; right=27 L10 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=4; right=27 L11 normalized_palindrome: right -= 1 text='A man, a plan, a canal: Panama'; left=4; right=26 ... 20 intermediate state changes omitted ... L11 normalized_palindrome: right -= 1 text='A man, a plan, a canal: Panama'; left=16; right=17 L5 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=17; right=17 L10 normalized_palindrome: left += 1 text='A man, a plan, a canal: Panama'; left=18; right=17 L11 normalized_palindrome: right -= 1 text='A man, a plan, a canal: Panama'; left=18; right=16
Result: True. Ignoring spaces, punctuation and case leaves matching endpoints.
normalized_palindrome("!!!")
L2 normalized_palindrome: left, right = 0, len(text) - 1 text='!!!'; left=0; right=2 L5 normalized_palindrome: left += 1 text='!!!'; left=1; right=2 L5 normalized_palindrome: left += 1 text='!!!'; left=2; right=2 L10 normalized_palindrome: left += 1 text='!!!'; left=3; right=2 L11 normalized_palindrome: right -= 1 text='!!!'; left=3; right=1
Result: True. The meaningful sequence is empty.
Why the solution is correct
Skipping removes only characters the contract ignores. Matching the next meaningful endpoints is necessary for any palindrome, and after a match the interior is the remaining subproblem. Exhausting it proves all required pairs matched.
Time and space
O(n) time and O(1) auxiliary space for ASCII input.
Common mistakes
Skipping with if instead of while fails on several punctuation marks. Unbounded skipping can move a pointer outside the string.
Reasoning card
State: left and right delimit unexamined characters. All meaningful pairs outside them have already matched.
Invariant: Pointers always refer to the next visible characters.
Decision to rehearse: Repeatedly skip invalid positions before comparing.
Finish: Reject a mismatch or move both pointers inward
Transfer exercise
Permit deletion of at most one meaningful character. At the first mismatch, which two subproblems must be considered?