Valid Palindrome
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.
- 1 <= s.length <= 2 * 10⁵
- s consists of printable ASCII characters
- Ignore all non-alphanumeric characters
- Comparison is case-insensitive
s = "A man, a plan, a canal: Panama"trues = "race a car"falses = " "trues = "0P"falseWe want to determine if a given phrase reads the exact same forward and backward. However, real-world text is full of "noise" like spaces, punctuation, symbols, and varying capitalizations. To find the underlying symmetry, we must ignore all non-alphanumeric characters and treat uppercase and lowercase letters as identical. How can we check for this symmetry efficiently?
The most straightforward approach is to filter out the noise first. We create a brand new string, copying over only the alphanumeric characters in their lowercase forms. Once we have this clean string, we compare it to a fully reversed copy of itself. If they are identical, the phrase is a palindrome.
While this runs in linear time, it forces us to allocate memory for a whole new cleaned string and its reverse. If the input string is very large, this O(N) extra space is a wasteful memory overhead.
# Filter and lowercase
cleaned = [char.lower() for char in s if char.isalnum()]
cleaned_str = "".join(cleaned)
# Compare with reverse
return cleaned_str == cleaned_str[::-1]To eliminate the memory overhead entirely, we can verify the symmetry in-place using two converging pointers:
- The Left Explorer (left): Starts at the very beginning of the string and marches inward to the right.
- The Right Explorer (right): Starts at the very end of the string and marches inward to the left.
By skipping noise on the fly, we don't need to copy any characters.
The two pointers step inward toward each other. If a pointer lands on a non-alphanumeric character (like a space or comma), it simply skips past it. Once both pointers are resting on valid alphanumeric characters, we compare them case-insensitively.
If they match, we continue our inward march. If they mismatch at any point, we can early-exit and return false immediately, saving unnecessary work. If the pointers meet or cross in the center without any mismatch, the symmetry is fully verified.
left = 0
right = len(s) - 1
while left < right:
# Skip noise from the left
if not s[left].isalnum():
left += 1
# Skip noise from the right
elif not s[right].isalnum():
right -= 1
else:
# Compare alphanumeric characters case-insensitively
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return TrueValid Palindrome Visualization
Checking symmetry while ignoring symbols
Key Insight
We can skip non-alphanumeric characters by iterating the pointers past them before performing the comparison.
Pointers Strategy
Converge from both ends. If characters (lower-cased) don't match, return false immediately. If they meet, return true.