Last Updated: March 20, 2026
This isn't a straightforward "is this string a palindrome?" check. There are two preprocessing steps baked into the problem: ignore case and ignore non-alphanumeric characters. The string "A man, a plan, a canal: Panama" has spaces, commas, and a colon, but once you strip all of that away and lowercase everything, you get "amanaplanacanalpanama", which reads the same forwards and backwards.
The key question is: do you actually need to build that cleaned-up string, or can you skip over the junk characters on the fly? That distinction is exactly what separates the two approaches below.
1 <= s.length <= 2 * 10^5 → With up to 200,000 characters, we need O(n) or better. Both approaches run in O(n) time, but they differ in space usage.s consists only of printable ASCII characters → We need to handle letters, digits, spaces, punctuation, and symbols. No Unicode edge cases to worry about.The most straightforward approach is to do exactly what the problem says: strip out everything that isn't a letter or digit, convert to lowercase, and then check if the result is a palindrome.
How do you check if a string is a palindrome? Reverse it and see if it matches the original. If the cleaned string equals its reverse, we have a palindrome.
This approach works but allocates extra strings. What if we compared characters directly from both ends of the original string, skipping non-alphanumeric characters as we go?
Instead of building a cleaned string, we can work directly on the original. Place one pointer at the start and one at the end. Move them toward each other, but skip over any character that isn't alphanumeric. When both pointers land on valid characters, compare them case-insensitively. If they ever mismatch, it's not a palindrome. If the pointers cross without a mismatch, it is.
This avoids allocating any extra strings. We're doing the filtering and comparison in a single pass.
A palindrome is symmetric around its center. If you compare the first valid character with the last valid character, the second with the second-to-last, and so on, you're checking that symmetry directly. By skipping non-alphanumeric characters, you're effectively comparing the same cleaned string that Approach 1 builds, but without ever allocating it.
Each character is visited at most once by each pointer, so the total work is still O(n). But the space drops from O(n) to O(1) since we only use two integer variables.
left to 0 and right to the last index of the string.left < right:left forward while it points to a non-alphanumeric character.right backward while it points to a non-alphanumeric character.left >= right, break (all valid characters have been compared).s[left] and s[right].left forward and right backward.