Browse curriculum

Valid Palindrome II

Decide whether a string becomes a palindrome after deleting at most one character, by trying both deletions only at the first mismatch.

Problem Understanding

Valid Palindrome II: return true if s can be a palindrome after deleting at most one character.

Examples: "aba" is already a palindrome; "abca" becomes "aba" or "aca" by deleting one character; "abc" cannot be fixed with one deletion.

The Intuition: Spend the Deletion at the First Mismatch

Check from the outside in, as in Valid Palindrome, with two pointers l and r. While s[l] == s[r], those characters are fine and both pointers move inward.

At the first mismatch, one of s[l] or s[r] must be the extra character, since every pair outside already matches. So there are only two candidates:

  • Delete s[l]: is s[l+1..r] a palindrome?
  • Delete s[r]: is s[l..r−1] a palindrome?

If either is, return true. If the pointers meet without a mismatch, no deletion was needed.

Interactive Walkthrough

Matched pairs are shaded from the outside in. At the first mismatch, watch each deletion get checked in turn: the deleted character is struck through and the rest is compared from both ends.

Dry Run Table

Input: "abca"

l r Compare Action
0 3 a = a move in
1 2 b ≠ c first mismatch
— — delete b: "c" palindrome

Return true.

The Solution Template

Two pointers, with a palindrome check for each possible deletion.

Valid Palindrome II Code
1var validPalindrome = function(s) {
2 const isPal = (l, r) => {
3 while (l < r) {
4 if (s[l++] !== s[r--]) return false;
5 }
6 return true;
7 };
8 let l = 0, r = s.length - 1;
9 while (l < r) {
10 if (s[l] !== s[r]) return isPal(l + 1, r) || isPal(l, r - 1);
11 l++;
12 r--;
13 }
14 return true;
15};

Edge Cases & Common Mistakes

  • Already a palindrome: no deletion needed; return true.
  • Trying only one side: "eeeed" fails if you only delete the left character; check both.
  • Allowing more than one deletion: once you are inside a check, any further mismatch means false.
  • Re-checking the whole string: only the part between l and r needs checking after the first mismatch.

Summary

Move two pointers inward while the ends match. At the first mismatch, the deleted character must be s[l] or s[r], so return whether s[l+1..r] or s[l..r−1] is a palindrome. If there is no mismatch, return true. O(n) time, O(1) space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Walk inward

    Move l and r toward each other while s[l] == s[r].

  2. At the first mismatch

    Return whether s[l+1..r] or s[l..r−1] is a palindrome.

  3. No mismatch

    If the pointers meet, s is already a palindrome: return true.

Frequently asked questions

Why only try deletions at the first mismatch?

Every pair outside the first mismatch already matches, so deleting a character there would only break a good pair. One of the two mismatched characters has to go, so those are the only two deletions worth checking.


Why is it O(n) and not O(n²)?

The main walk and the two follow-up checks each cover the string at most once, so the total work is a constant number of passes. Trying every single deletion and re-checking would be O(n²).


Do the follow-up checks start over from the ends?

No. Everything outside l..r has already matched, so the checks only look at s[l+1..r] and s[l..r−1].

Applying for software engineering roles? Free, no sign-up:

AI resume checker — match your resume to the job description