返回
力扣刷题 | 680:验证回文字符串 II:高级攻略和深入解析
前端
2023-11-10 21:49:14
导言
回文,即从左到右读和从右到左读都相同的字符串,在计算机科学和自然语言处理中都有着广泛的应用。在力扣平台上,680 题「验证回文字符串 II」要求我们在原有的「验证回文字符串 I」基础上更进一步,允许删除一个字符来判断字符串是否可以成为回文。这道题考察了算法、字符串操作和动态规划等多方面的知识,需要我们深入理解回文的本质和解决问题的策略。
问题
给定一个字符串 s
,判断在至多删除一个字符的情况下,它是否可以成为回文。
示例
示例 1:
输入:s = "aba"
输出:true
示例 2:
输入:s = "abca"
输出:true
示例 3:
输入:s = "abc"
输出:false
算法解析
我们知道,回文的核心特征是左右对称。因此,我们可以从字符串的两端开始比较,如果遇到不匹配的字符,则尝试删除左指针或右指针指向的字符,并继续比较。需要注意的是,如果删除了字符,则需要更新左右指针的位置。
算法步骤如下:
- 初始化左指针
left
为 0,右指针right
为s.length - 1
。 - 循环比较
s[left]
和s[right]
:- 如果相等,则
left++
和right--
。 - 如果不相等,则尝试删除左或右指针指向的字符:
- 删除
s[left]
:left++
。 - 删除
s[right]
:right--
。
- 删除
- 如果删除字符后仍然不相等,则返回
false
。
- 如果相等,则
- 如果循环结束时
left
指针位于right
指针的右边或相等,则返回true
。
动态规划解决方案
除了暴力枚举删除字符的所有可能情况外,我们还可以使用动态规划来优化算法。动态规划的思想是将问题分解成更小的子问题,然后逐步求解子问题,最后得到整个问题的解。
对于本题,我们可以定义一个二位数组 dp[i][j]
,其中 i
表示字符串的左端点,j
表示字符串的右端点。dp[i][j]
表示在删除了至多一个字符的情况下,字符串 s[i:j+1]
是否可以成为回文。
动态规划的状态转移方程如下:
dp[i][j] = true # 如果 s[i:j+1] 本身就是回文
dp[i][j] = dp[i+1][j-1] # 如果 s[i] 和 s[j] 相等,且 s[i+1:j] 可以成为回文
dp[i][j] = dp[i+1][j] 或 dp[i][j-1] # 如果 s[i] 和 s[j] 不相等,尝试删除 s[i] 或 s[j]
最终,我们只需要判断 dp[0][s.length - 1]
是否为 true
即可。
示例代码
暴力枚举法:
def valid_palindrome_ii(s):
"""
暴力枚举法
:param s: str
:return: bool
"""
n = len(s)
for i in range(n):
# 尝试删除第 i 个字符
t = s[:i] + s[i+1:]
if t == t[::-1]:
return True
return False
动态规划法:
def valid_palindrome_ii(s):
"""
动态规划法
:param s: str
:return: bool
"""
n = len(s)
dp = [[False] * n for _ in range(n)]
# 初始化回文长度为 1 的子串
for i in range(n):
dp[i][i] = True
# 填补动态规划表
for l in range(2, n+1):
for i in range(n-l+1):
j = i + l - 1
if s[i] == s[j]:
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j] or dp[i][j-1]
return dp[0][n-1]