Practice problem
Valid Palindrome
EasySolve in Python · graded against 3 sample tests and a hidden test set in your browser · Published
Interactive solution
Solve Valid Palindrome
Run tries the first sample. Submit checks every sample and additional cases. Your code is saved in this browser.
Press Esc then Tab to move keyboard focus out of the code editor.
Output will appear here after you run your code.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, Madam, in Eden: I'm Adam. Picture two pointers, one starting at the first
character (index 0, M) and one at the last character (index 23, the final m). Both are letters, so
we lowercase and compare them: m equals m, a match, and both pointers move one step inward. This
continues character by character — a/a, then d/d, then a/A (equal once lowercased) — until
the right pointer lands on the space at index 19. A space isn't a letter or digit, so the right pointer
skips over it and lands on m at index 18, which matches the left pointer's m at index 4. Now the
left pointer has to skip too: it steps past the comma at index 5 and the space at index 6 to reach i
at index 7, while the right pointer steps past the apostrophe at index 17 to reach I at index 16 —
another match. Next the right pointer sits on the space at index 15 and then the colon at index 14,
skips both, and lands on n at index 13, which matches the left pointer's n at index 8. One more
skip on the left, past the space at index 9, brings E at index 10 up against e at index 12. The
pointers then meet at the d in the middle (index 11). 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 capitalMthat opensMadamlining up with the lowercasemthat endsAdam— 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:inEden: I'm. 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.
Constraints
- The input line has between 0 and 1,000 characters.
- The line may contain uppercase and lowercase English letters, digits, spaces, and punctuation characters.
- Only letters and digits count toward the comparison; case is ignored, so 'A' and 'a' are treated as the same character.
Target complexity
Time: O(n) — the two pointers together move across the string once, so every character is visited a constant number of times as the pointers step inward.
Space: O(1) — the two-pointer approach only needs a fixed number of index variables and does not build any extra copy of the string.
Ideas this uses
Sample tests
Sample 1
Input
Madam, in Eden: I'm Adam
Expected output
true
Removing spaces and punctuation and lowercasing everything gives "madaminedenimadam", which reads the same forwards and backwards.
Sample 2
Input
Step on pets
Expected output
false
Removing the spaces and lowercasing gives "steponpets"; reversed that is "stepnopets". The first four letters agree, but the fifth character is "o" one way and "n" the other, 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.