华为 OD 训练营 · 题解精讲
LC680. 验证回文串II
题目描述
给你一个字符串 s,最多 可以从中删除一个字符。 请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false 。 示例 1: 输入:s = "aba" 输出:true 示例 2: 输入:s = "abca" 输出:true 解释:你可以删除字符 'c' 。 示例 3: 输入:s = "abc" 输出:false 提示: 1 <= s.length <= 10^5 s 由小写英文字母组成
题目解析
好的,没问题!作为你的 AlgoMooc 算法老师,我们一起来把这个“验证回文串II”的问题彻底搞懂。
---
### 题目在说什么
简单说,就是给你一个字符串,比如 "abca"。你要判断它是不是一个“回文串”。什么是回文串?就是正着读和反着读都一样,比如 "aba"。
但这里有个特殊规则:你最多可以删除一个字符。也就是说,如果原字符串本身是回文,那当然好。如果不是,你可以试着删掉其中一个字符,看看剩下的能不能变成回文。如果可以,就返回 true,否则返回 false。
举个例子:
"aba"本身就是回文,不用删,返回true。"abca"不是回文,但你可以删掉中间的'c',剩下"aba"就是回文了,所以返回true。"abc"不是回文,你删掉任何一个字符('a'、'b'或'c'),剩下的"bc"、"ac"或"ab"都不是回文,所以返回false。
---
### 思路是怎么来的
想象一下,你面前有一排人,他们手里举着字母牌。你要检查他们举的字母是不是左右对称的(回文)。你从最左边和最右边开始,同时往中间走,边走边比较左右两个人的字母是否相同。
第一步:正常检查回文 你从左右两端开始,比如 "abca":
- 左指针
i指向'a'(索引0),右指针j指向'a'(索引3)。它们相等,继续。 - 左指针移到
'b'(索引1),右指针移到'c'(索引2)。它们不相等!
关键问题来了: 当遇到不相等时,我们该怎么办?题目说可以删除一个字符。所以,你有两个选择: 1. 删除左边的那个字符(即 'b'),然后检查剩下的子串 "ca" 是不是回文。 2. 删除右边的那个字符(即 'c'),然后检查剩下的子串 "ba" 是不是回文。
生活化比喻: 想象你正在检查一排人是不是对称。走到中间,发现左右两个人举的字母不一样。这时候,你可以让其中一个人“出列”(删除)。你可以选择让左边的人出列,然后看看剩下的人是不是对称;或者让右边的人出列,再看看剩下的人是不是对称。只要其中一种情况能对称,那整个任务就成功了。
为什么只检查这两种情况? 因为如果删除一个字符能让整个字符串变成回文,那这个被删除的字符一定是第一次出现不相等的那一对中的一个。为什么?因为在这对字符之前,左右都是对称的,删除这对之外的任何字符,都不会影响已经对称的部分,也无法解决当前的不相等问题。所以,我们只需要尝试删除这两个字符中的一个,然后检查剩下的子串。
具体做法: 1. 用两个指针 i 和 j 分别指向字符串的开头和结尾。 2. 当 i < j 时,比较 s[i] 和 s[j]。 3. 如果相等,i++,j--,继续比较。 4. 如果不相等,我们有两种尝试:
- 尝试删除
s[i](即跳过它),检查子串s[i+1...j]是不是回文。 - 尝试删除
s[j](即跳过它),检查子串s[i...j-1]是不是回文。
5. 如果这两种尝试中任何一种能成功(即子串是回文),那么整个字符串就可以通过删除一个字符变成回文,返回 true。 6. 如果两种都失败,返回 false。
---
### 代码逐步拆解
我们来看一段 Python 代码,我会一步一步解释。
class Solution:
def validPalindrome(self, s: str) -> bool:
# 定义一个辅助函数,用来检查一个子串是否是回文
def is_palindrome(sub_s):
# 用两个指针,一个从左边开始,一个从右边开始
left, right = 0, len(sub_s) - 1
while left < right:
if sub_s[left] != sub_s[right]:
return False
left += 1
right -= 1
return True
# 主逻辑:用双指针从两端向中间检查
i, j = 0, len(s) - 1
while i < j:
# 如果当前两个字符相等,继续向中间移动
if s[i] == s[j]:
i += 1
j -= 1
else:
# 如果不相等,尝试删除左边或右边的字符
# 删除左边:检查子串 s[i+1:j+1] 是不是回文
# 删除右边:检查子串 s[i:j] 是不是回文
# 注意:Python切片是左闭右开,所以 s[i+1:j+1] 包含索引 i+1 到 j
# 而 s[i:j] 包含索引 i 到 j-1
return is_palindrome(s[i+1:j+1]) or is_palindrome(s[i:j])