public boolean isPalindrome(String s) { int l = 0; int r = s.length() - 1; while (l < r) { while (l < r && !Character.isLetterOrDigit(s.charAt(l))) { l++; } while (l < r && !Character.isLetterOrDigit(s.charAt(r))) { r--; } if (Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r))) { return false; } l++; // pair matched, move inward r--; } return true;}
- l
- —
- r
- —
- s.charAt(l)
- —
- s.charAt(r)
- —
- pairs matched
- 0
Why it works
- Brute force builds the cleaned string (lowercase, letters and digits only) and compares it with its reverse. Also O(n), but it copies the string: O(n) extra space.
- Two pointers compare in place. Invariant: every letter outside
[l, r]already matches its mirror. Each match moves both ends inward, so the unchecked middle shrinks by 2. - Skip, don't delete. The inner loops hop over spaces and punctuation. Nested loops, yet every index is visited at most once: O(n) time, O(1) space.
- Pitfall: keep
l < rin the inner loops, or a string like".,"runs a pointer off the end. AndCharacter.isLetterOrDigitincludes digits:"0P"is not a palindrome. - Exit early: the first mismatch answers the question. The characters between the pointers are never read.
- Follow-up, Valid Palindrome II (680): you may delete one character. At the first mismatch, try skipping
s.charAt(l)ors.charAt(r)and check the rest with the same two pointers.