10.13 Valid Palindrome II
10.13.1 Problem Metadata
- Platform: LeetCode
- Problem ID: 680
- Difficulty: Easy
- URL: https://leetcode.com/problems/valid-palindrome-ii/
- Tags: Meta
- Techniques: Two Pointers, String
10.13.2 Description
Given a string s, return true if the s can be palindrome after deleting at most one character from it.
10.13.3 Examples
Example 1:
Input: s = "aba"
Output: true
Explanation: The string is already a palindrome.
Example 2:
Input: s = "abca"
Output: true
Explanation: You could delete the character 'c'.
Example 3:
Input: s = "abc"
Output: false
Explanation: No single deletion can make this a palindrome.
10.13.4 Constraints
- \(1 \le\) s.length \(\le\) \(10^5\)
sconsists of lowercase English letters only
10.13.5 Solution - Two Pointers with One Skip Allowed
10.13.5.1 Walkthrough
This problem extends the classic palindrome check by allowing at most one character deletion. The solution uses a two-pointer approach with a key insight: when we find a mismatch, we try skipping either the left or right character and check if the remaining substring forms a palindrome.
Core Strategy:
- Start with standard palindrome check: Use two pointers from both ends moving inward
- On first mismatch: We have two options:
- Skip the left character and check if
s[left+1...right]is a palindrome - Skip the right character and check if
s[left...right-1]is a palindrome
- Skip the left character and check if
- Early termination: If either skip option works, return
trueimmediately - No redundant work: The helper method validates the entire remaining substring, so no need to continue the main loop
Why This Works:
- If the string is already a palindrome, we never encounter a mismatch → return
true - If we can make it a palindrome by removing one character, at least one of the two skip options will succeed
- If both skip options fail, no single deletion can make it a palindrome → return
false
Visual Example with s = "abca":
Initial: l=0, r=3
Step 1: Compare s[0] vs s[3]
String: [a] b c [a]
↑ ↑
l r
'a' == 'a' ✓ → Continue
l=1, r=2
Step 2: Compare s[1] vs s[2]
String: a [b] [c] a
↑ ↑
l r
'b' != 'c' ✗ → Found mismatch!
Try two options:
Option 1: Skip left (skip 'b')
Check isPalindrome(s, 2, 2) → substring "c"
l=2, r=2 → l >= r → return true ✓
Since Option 1 succeeded, return true immediately!
Final result: true (delete 'b' or 'c' to make palindrome)
Visual Example with s = "abc":
Initial: l=0, r=2
Step 1: Compare s[0] vs s[2]
String: [a] b [c]
↑ ↑
l r
'a' != 'c' ✗ → Found mismatch!
Try two options:
Option 1: Skip left (skip 'a')
Check isPalindrome(s, 1, 2) → substring "bc"
l=1, r=2: 'b' != 'c' → return false ✗
Option 2: Skip right (skip 'c')
Check isPalindrome(s, 0, 1) → substring "ab"
l=0, r=1: 'a' != 'b' → return false ✗
Both options failed → return false
Final result: false (no single deletion works)
Visual Example with s = "aba":
Initial: l=0, r=2
Step 1: Compare s[0] vs s[2]
String: [a] b [a]
↑ ↑
l r
'a' == 'a' ✓ → Continue
l=1, r=1
Step 2: l >= r (pointers crossed)
Exit loop → return true
Final result: true (already a palindrome, no deletion needed)
Key Optimization:
The helper method isPalindrome(String s, int l, int r) uses indices instead of creating substrings. This avoids string allocation overhead and improves performance:
- ✗ Slower: isPalindrome(s.substring(l, r)) - Creates new string object
- ✓ Faster: isPalindrome(s, l, r) - Uses indices on original string
10.13.5.2 Analysis
- Time Complexity: O(n)
- Best case: O(n/2) ≈ O(n) when string is already a palindrome
- Single pass comparing characters from both ends
- No mismatch encountered
- Worst case: O(n/2) + O(n/2) ≈ O(n) when mismatch found near center
- Main loop: O(n/2) comparisons until first mismatch
- Helper check: O(n/2) comparisons to verify skip option
- Total: O(n/2 + n/2) = O(n)
- Each character is visited at most twice (once in main loop, once in helper)
- Best case: O(n/2) ≈ O(n) when string is already a palindrome
- Space Complexity: O(1)
- Only uses pointer variables (
l,r) - No extra data structures or substring allocation
- Recursive calls would add O(n) stack space, but this solution is iterative
- Only uses pointer variables (
10.13.5.3 Implementation Steps
Initialize two pointers:
- Left pointer:
lat index 0 - Right pointer:
rat index s.length() - 1
- Left pointer:
Main palindrome check loop: While
l < r- Compare characters at
s.charAt(l)ands.charAt(r) - If match: Move pointers inward (
l++,r--) - If mismatch: Try both skip options
- Compare characters at
Handle mismatch (only happens once):
- Try skipping left:
isPalindrome(s, l+1, r) - Try skipping right:
isPalindrome(s, l, r-1) - Return
trueif either option succeeds (using||short-circuit) - Return
falseif both options fail
- Try skipping left:
No mismatch encountered: Return
true(already palindrome)Helper method
isPalindrome(String s, int l, int r):- Takes indices instead of substring for efficiency
- Standard two-pointer palindrome check
- Returns
trueif substrings[l...r]is palindrome
10.13.5.4 Code - Java
class Solution {
public boolean validPalindrome(String s) {
int l = 0, r = s.length() - 1;
while (l < r) {
if (s.charAt(l) != s.charAt(r)) {
// Found mismatch - try skipping either left OR right
// If EITHER option works, the string can be a palindrome
return isPalindrome(s, l+1, r) || isPalindrome(s, l, r-1);
}
l++;
r--;
}
return true; // No mismatches found - already a palindrome
}
// Helper method to check if substring s[l...r] is palindrome
// Uses indices instead of creating substring for better performance
private boolean isPalindrome(String s, int l, int r) {
while (l < r) {
if (s.charAt(l) != s.charAt(r)) {
return false;
}
l++;
r--;
}
return true;
}
}