题目描述
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
示例
示例 1:
输入: s = “aba”
输出: true
示例 2:
输入: s = “abca”
输出: true -------解释: 你可以删除c字符。
示例 3:
输入: s = “abc”
输出: false
题解
1、双指针
我怕第一次写的,但是很麻烦
def validPalindrome(s):
left, right = 0, len(s)-1
count = 0 #记录移除次数
while left < right:
if s[left] == s[right]: # 满足回文要求,指针继续移动
left += 1
right -= 1
elif s[left+1] == s[right]:
count += 1
left += 2
right -= 1
elif s[left] == s[right-1]:
count += 1
right -= 2
left += 1
else:
return False
if count >= 2:
return False
return True
第一次,我试图列举出所有情况,将其归类为三种情况。如图:
1)如果要删除的元素在整个字符串的左边
那么当前left指针的下一位应该和此时的right指针值得字母相同
对应elif s[left+1] == s[right]:
2)如果要删除的元素在整个字符串的右边elif s[left] == s[right-1]:
3)要删除的字符串在整个字符正中间两位:
这种情况可以归为上述情况中的任意一种
那么如果还有其他的情况,直接说明不能只删除一个字母达到回文字符串
对应else:
的部分
错因分析
在分支结构有条件判断时出现错误。
if…elif…elif …并不是并列的情况,一定是第一个elif的条件不满足,才执行第二个elif
而“删除部分在字串左边”和“删除字母在字串右边”是两种并列的情况
这样就会导致s[left + 1] == s[right]
和s[left] == s[right - 1]
这两种情况同时满足时,就会出现错误的判断。
如字串"“lcuppucul”",用上出代码会判断为第一个elif s[left + 1] == s[right]的情况,即删除部分在字串左边。但实际上删除的应该是在右边。
措施:
添加两种情况同时满足的讨论,要注意排除字母在正中间两位的情况。
即s[left + 1] == s[right] and s[left] == s[right] and left != right - 1
2、暴力枚举
思路分析
一次列举出s中删除一个字母之后的字符串,看删除一个字母之后是不是回文字符串
代码实现
def is_Palindrome(s):
left, right = 0 ,len(s) - 1
while left <= right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
def func(s):
for i in range(len(s) - 1):
if is_Palindrome(s[:i] + s[i+1: ]): # 如果出现回文子串,直接返回
return True
return False
算法分析
1)时间复杂度:O(n²),for 循环遍历一次—n,每次切片创建新字符串-----2n
2)空间按复杂度:O(n)
3、贪心算法
思路分析
如果最外面两个字母相等,则可以直接删除两端的两个字母。直接判断删除两端字母后的子串是否为回文。
每次判断两个指针指向的字符是否相同,如果相同,则更新指针,
left += 1,right -=1
,然后判断更新后的指针范围内的子串是否是回文字符串。如果两个指针指向的字符不同,则两个字符中必须有一个被删除,分别判断删除左字母和右字母后是不是回文子串,有一个满足情况即可。
代码实现
def validPalindrome( s):
def isPalindrome(low, high):
# t1, t2 = low, high # 不用重新赋值,因为不会修改外部变量
while low < high:
if s[low] != s[high]:
return False
low += 1
high -= 1
return True
left, right = 0, len(s)-1
while left <= right:
if s[left] == s [right]:
left += 1
right -= 1
else:
# 如果删除左边的、删除右边的 剩余的都不是回文,那么就不满足,返回False
return isPalindrome(left + 1, right) or isPalindrome(left, right - 1)
return True
算法分析
1、在函数内部创建另一个函数,形成闭包。子函数不会修改原函数中的变量值。古不用重新赋值t1,t2
2、验证回文,不要直接套用之前的代码。灵活变通。字串共用同一个s,只需要传入索引即可。
否则传入新字符串,
return isPalindrome(s[left + 1: right]) or isPalindrome(s[left: right - 1])
用切片,浪费时间空间
3、return isPalindrome(left + 1, right) or isPalindrome(left, right - 1)
等价于
if isPalindrome(left + 1, right) == False and isPalindrome(left, right - 1) == False:
return False
else:
return True