Valid Palindrome II
Table of Contents + β
Valid Palindrome II is a clean two-pointer question. It tests something simple. Can you check a string from both ends and handle one mismatch in a smart way? The interviewer wants to see if you can split into two small checks instead of trying every deletion.
π― The Problem
You get a string and one allowed deletion. Decide if the string can become a palindrome. The rules:
- A palindrome reads the same forward and backward, like
racecaroraba. - You may delete at most one character.
- Return true if some single deletion makes it a palindrome.
- An already-palindrome string is also true, since deleting nothing is allowed.
- If no single deletion can fix it, return false.
For the string abca, deleting the c gives aba, which reads the same both ways. So the answer is true. One deletion was enough.
Input: s = "abca"Output: true
Explanation: delete 'c' to get "aba", which is a palindrome.Here is the idea. Two pointers start at the ends and walk inward, comparing letters.
π’ Approach 1: Try Every Deletion (Brute Force)
Delete one character at a time and test the result.
The idea:
- Delete the first character and check if the rest is a palindrome.
- Then delete the second, and so on for every position.
- Also check the original with no deletion.
Why it is weak:
- For each deletion you build a new string and check the whole thing.
- That check is O(n), done for every one of the n positions.
- Total time is O(nΒ²). Correct but slow.
Here is the brute-force code for that idea:
def valid_palindrome(s): if s == s[::-1]: return True
for i in range(len(s)): candidate = s[:i] + s[i + 1:] if candidate == candidate[::-1]: return True
return Falseβ‘ Approach 2: Two Pointers With One Skip (Best)
The idea in one line: walk in from both ends, and at the first mismatch spend your single deletion on the left letter or the right letter.
The idea:
- Put one pointer at the start and one at the end.
- Walk them inward and compare the two letters each step.
- While letters match, keep moving both in.
How it works:
- The trouble starts only at the first mismatch.
- That is where you spend the one allowed deletion.
- Try skipping the left letter: check if the range from left plus one to right is a palindrome.
- Try skipping the right letter: check if the range from left to right minus one is a palindrome.
- If either range is a palindrome, the answer is true. If neither works, false.
Why it is fast:
- The palindrome check is just another two-pointer walk with no skips.
- The extra check runs only once, at the first mismatch.
- Total time is O(n) and space is O(1).
Here is the dry-run for abca.
Steps to Solve
- Put a left pointer at the start and a right pointer at the end.
- While left is before right, compare the two letters.
- If they match, move both pointers inward and continue.
- If they do not match, check two things: skip the left letter, or skip the right letter.
- Each check is a plain palindrome test on the smaller range.
- Return true if either check passes. If the loop finishes with no mismatch, return true.
This Python version uses an inner helper to check any slice of the string.
def valid_palindrome(s): def is_range(left, right): while left < right: if s[left] != s[right]: # mismatch in this range return False left += 1 right -= 1 return True
left = 0 right = len(s) - 1 while left < right: if s[left] != s[right]: # spend the one deletion: skip left OR skip right return is_range(left + 1, right) or is_range(left, right - 1) left += 1 right -= 1 return True # already a palindrome
s = "abca"print("true" if valid_palindrome(s) else "false")The output of the above code will be:
trueLet us walk through the Python version line by line. The clever part is what happens at the mismatch.
def is_range(left, right): is a small helper. It checks if the part of the string between two positions reads the same both ways. It is a plain two-pointer palindrome test with no deletions. We reuse it for both skip choices.
left = 0 and right = len(s) - 1 set the main pointers at the two ends of the string.
while left < right: walks the pointers inward.
if s[left] != s[right]: catches the first mismatch. This is the only place we ever spend the deletion. We return is_range(left + 1, right) or is_range(left, right - 1). The first call skips the left letter. The second call skips the right letter. If either smaller range is a palindrome, the whole answer is true. The or returns true if at least one passes.
left += 1 and right -= 1 move both pointers in when the letters match. If the loop finishes without ever hitting a mismatch, the string was already a palindrome. So we return True. We never needed the deletion at all.
β±οΈ Time and Space Complexity
The brute force tries every deletion and checks the whole string each time, so it is O(nΒ²) time. The two-pointer way walks the string once, and at the single mismatch it does one extra range check. That extra check is O(n) but it happens only once. So the whole thing is O(n) time and O(1) space.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Brute force (try every deletion) | O(nΒ²) | O(n) |
| Two pointers with one skip | O(n) | O(1) |
Tip
The key insight is that you only ever spend the deletion at the first mismatch. Before that point the string already matches. So you do not need to try deleting every character. Say this clearly in the interview.
π§© Key Takeaways
- β Walk two pointers inward and compare letters from both ends.
- β The first mismatch is the only place you spend the one deletion.
- β At the mismatch, try skipping the left letter or the right letter.
- β Each skip turns into a plain palindrome check on a smaller range.
- β The two-pointer method runs in O(n) time with O(1) extra memory.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does Valid Palindrome II allow you to do?
Why: You may delete at most one character and then ask if the result is a palindrome.
- 2
When do we spend the one allowed deletion?
Why: Up to the first mismatch the string already matches, so the deletion is only needed there.
- 3
At the mismatch, what two checks do we make?
Why: We test the range after skipping the left letter and the range after skipping the right letter.
- 4
What is the time and space complexity of the two-pointer solution?
Why: One inward walk is O(n), the single extra range check is also O(n) but runs once, and we keep only a few variables.