Valid Palindrome II
Difficulty: Easy Source: NeetCode
Problem
Given a string
s, returntrueif the string can be a palindrome after deleting at most one character from it.Example 1: Input:
s = "aba"Output:trueExample 2: Input:
s = "abca"Output:true(delete'c')Example 3: Input:
s = "abc"Output:falseConstraints:
1 <= s.length <= 10^5sconsists only of lowercase English letters
Prerequisites
Before attempting this problem, you should be comfortable with:
- Valid Palindrome (LeetCode 125) — checking if a string is a palindrome with two pointers
- Two Pointers — moving inward from both ends and branching on a mismatch
1. Brute Force
Intuition
Try deleting every character one at a time and check if the resulting string is a palindrome. The first deletion that produces a palindrome means we return True. If no single deletion works, return False. Simple to understand but slow — we’re doing n palindrome checks each costing O(n).
Algorithm
- Define a helper
is_palindrome(t)that returns whether stringtreads the same forward and backward. - If
sis already a palindrome, returnTrue. - For each index
ifrom0tolen(s) - 1:- Build
t = s[:i] + s[i+1:](delete character ati). - If
is_palindrome(t), returnTrue.
- Build
- Return
False.
Solution
def validPalindrome(s):
def is_palindrome(t):
return t == t[::-1]
if is_palindrome(s):
return True
for i in range(len(s)):
if is_palindrome(s[:i] + s[i + 1:]):
return True
return False
print(validPalindrome("aba")) # True
print(validPalindrome("abca")) # True
print(validPalindrome("abc")) # False
Complexity
- Time:
O(n²) - Space:
O(n)
2. Two Pointers with One Skip
Intuition
Walk inward from both ends with two pointers, exactly like a normal palindrome check. The moment we hit a mismatch at positions left and right, we know we must skip one of those two characters. We check both options: does s[left+1 .. right] form a palindrome, or does s[left .. right-1]? If either does, we can fix the string with one deletion. We only ever need to branch once — if neither half is a palindrome, no single deletion can save us.
Algorithm
- Define a helper
is_palindrome(t, l, r)that checks ift[l..r]is a palindrome using two pointers. - Initialize
left = 0,right = len(s) - 1. - While
left < right:- If
s[left] == s[right]: move both pointers inward. - Else: return
is_palindrome(s, left + 1, right) or is_palindrome(s, left, right - 1).
- If
- Return
True(no mismatch found — already a palindrome).
Solution
def validPalindrome(s):
def is_palindrome(t, l, r):
while l < r:
if t[l] != t[r]:
return False
l += 1
r -= 1
return True
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return is_palindrome(s, left + 1, right) or is_palindrome(s, left, right - 1)
left += 1
right -= 1
return True
print(validPalindrome("aba")) # True
print(validPalindrome("abca")) # True
print(validPalindrome("abc")) # False
print(validPalindrome("deeee")) # True
Complexity
- Time:
O(n) - Space:
O(1)
Common Pitfalls
Skipping more than one character. The problem allows at most one deletion. Once you hit a mismatch, you must commit to one of the two choices and verify the remainder is a palindrome — you can’t keep skipping inside the helper.
Checking the wrong substrings on mismatch. When s[left] != s[right], the two candidate substrings are s[left+1 .. right] (skip left character) and s[left .. right-1] (skip right character). A common mistake is to check s[left+1 .. right-1], which skips both — that’s two deletions, not one.
Returning early on an already-valid palindrome. If the two-pointer walk completes without a mismatch, the string is already a palindrome, so return True without calling the helper.