Practice problem
Valid Palindrome
EasySolve in Python · graded against 3 sample tests and a hidden test set in your browser · Published
Problem Statement
You're given a single line of text that can mix letters, digits, spaces, and punctuation in any
combination of upper and lower case. Decide whether that line is a palindrome once you ignore everything
except letters and digits, and once you treat uppercase and lowercase letters as identical. Print true
if, after that cleanup, the line reads the same from left to right as it does from right to left, and
print false otherwise.
Input Format
The input is exactly one line: the string to check. That line can be empty (zero characters), and when it isn't empty it can include upper- and lowercase English letters, digits 0-9, spaces, and punctuation marks such as commas, periods, apostrophes, and colons. Read the whole line exactly as given — the spaces and punctuation are still part of the line even though the comparison itself will skip over them.
Output Format
Print exactly one line containing the word true or the word false, in lowercase and with no
surrounding quotes or extra text. Print true if the cleaned-up version of the input (letters and
digits only, lowercased) is a palindrome, and false if it is not.
Example Walkthrough
Use the first sample, A man, a plan, a canal: Panama. Picture two pointers, one starting at the first
character (index 0, A) and one at the last character (index 29, the final a). Both are letters, so
we lowercase and compare them: a equals a, a match, and both pointers move one step inward. This
continues character by character — m/m, then a/a, then n/n — until the left pointer lands on
the comma at index 5. A comma isn't a letter or digit, so the left pointer skips over it and the
following space, landing on a at index 7, which matches the right pointer's a. The same skipping
happens later on the right side too: as the right pointer moves inward it eventually sits on the space at
index 23 and then the colon at index 22, skips both, and lands on l at index 21, which matches the left
pointer's l. The two pointers keep stepping inward, matching pair after pair, until they meet at the
c in the middle (index 17). Since every pair that was compared matched and the pointers met without
ever finding a mismatch, the line is a palindrome, so we print true.
Approach
One way to solve this is to build a cleaned-up copy of the string first: walk through the original line
once, keep only the characters where .isalnum() is true, lowercase each of them, and collect the
results into a new string. Once you have that cleaned string, checking whether it's a palindrome is just
a matter of comparing it to its own reverse. This is easy to reason about, but it uses extra space
proportional to the length of the input, since you're storing a whole second copy of it.
The two-pointer technique avoids that extra copy. Keep one index starting at the front of the original
string and another starting at the back, and move them toward each other. At each step, if the character
under the left pointer isn't a letter or digit, advance just the left pointer past it without comparing
anything yet; do the same for the right pointer if its character isn't a letter or digit. Once both
pointers are sitting on a letter or digit, compare them case-insensitively — if they differ, you can stop
immediately and report false, since a single mismatch is enough to rule out a palindrome. If they
match, move both pointers one step further inward and repeat.
The moment the two pointers meet or cross, every pair of characters that could possibly mismatch has
already been checked and none of them did, so the string is confirmed to be a palindrome and you can
print true. Skipping non-alphanumeric characters in place, rather than removing them into a new string
first, is what keeps this version from needing any extra memory beyond the original input and a couple of
index variables.
Common Mistakes
- Filtering with a check that excludes digits. It's easy to write a condition that only keeps letters
(for example, checking
.isalpha()instead of.isalnum()), forgetting that digits also count as alphanumeric characters for this problem. A short input like0Pshows the difference clearly: correctly keeping both characters gives0p, which reversed isp0— not a match, so the answer isfalse— but dropping the digit leaves onlyp, a single character that trivially looks like a palindrome. - Comparing characters without lowercasing them first. If you compare
text[left]totext[right]directly instead of comparing their lowercased forms, a perfectly valid palindrome where the case differs between the two matching positions — like the capitalPinPanamalining up with a lowercasepelsewhere — will be reported as a mismatch even though it shouldn't be. - Skipping only one non-alphanumeric character at a time with a single
ifinstead of a loop. Punctuation and spaces often appear back-to-back, like the:incanal: Panama. If your pointer-skipping logic only steps past one such character before immediately trying to compare, it will end up comparing a space or punctuation mark against a letter and report a false mismatch — the skipping step needs to keep advancing the pointer until it actually lands on a letter or digit.
Sample tests
Sample 1
Input
A man, a plan, a canal: Panama
Expected output
true
Removing spaces and punctuation and lowercasing everything gives "amanaplanacanalpanama", which reads the same forwards and backwards.
Sample 2
Input
race a car
Expected output
false
Removing the spaces gives "raceacar"; reversed that is "racaecar", which does not match, so the line is not a palindrome.
Sample 3
Input
Was it a car or a cat I saw?
Expected output
true
Removing spaces and punctuation and lowercasing everything gives "wasitacaroracatisaw", which reads the same forwards and backwards.
Your solution
Run tries the first sample. Submit grades against every sample and the hidden tests. Your code is saved in this browser as you go.
Press Esc then Tab to move keyboard focus out of the code editor.
Output will appear here after you run your code.