1、验证回文串
题目描述:给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
输入: “A man, a plan, a canal: Panama”
输出: true
思路分析:(1)定义两个指针,一个从后往前遍历字符串,一个从前往后遍历,两个指针同时遍历,碰到不是字符就跳过,是字符就比较对应位置的字符是否一样。一直遍历到两个指针相遇或者前指针超过后指针。
代码如下
class Solution:
def isPalindrome(self, s: str) -> bool:
# if not s:return True
# else:
# i, j = 0, len(s)-1
# while i<=j:
# while not s[i].isalnum() and i<j:
# i += 1
# while not s[j].isalnum() and j>i:
# j -= 1
# if s[i].lower()==s[j].lower():
# i += 1
# j -= 1
# else:
# return False
# return True
# s = "".join(i for i in s if i.isalnum())
# p = s.lower()
# l, r = 0, len(p)-1
# while l<=r:
# if p[l]==p[r]:
# l += 1
# r -= 1
# else:return False
# return True
#
newstr = "".join(i for i in s if i.isalnum())
newstr = newstr.lower()
return newstr==newstr[::-1]
2、回文子串
题目描述:给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
输入: “abc”
输出: 3
解释: 三个回文子串: “a”, “b”, “c”.
思路分析动态规划
(1)我们定义dp[i][j]为字符串索引i到j的子串是否是子串,显然,如果dp[i+1][j-1]是回文子串,那么如果S[i]==S[j],则dp[i][j]是回文子串,否则不是回文子串。
我们以S="cabbac"为例,可以得到以下动态数组。
(2)从上面的动态数组可以看出,每一行只与上一行有关,因此我们可以定义一个一维数组。dp[j]表示从索引j到当前索引位置i是否是回文字符串。dp[j]与dp[j+1]有关。
代码如下
class Solution:
def countSubstrings(self, s: str) -> int:
#二维数组
# n = len(s)
# dp = [[False]*n for _ in range(n)]
# for i in range(n):
# dp[i][i]=True
# count = 0
# for k in range(1,n):
# for i in range(n):
# j = i+k
# if j<len(s):
# if (dp[i+1][j-1] and s[i]==s[j]) or (j-i==1 and s[i]==s[j]):
# dp[i][j]=True
# count += 1
# return count+n
#一维数组
cnt = 0
dp = [0]*(len(s))
for i in range(len(s)):
dp[i]=1
cnt +=1
for j in range(i):
if s[i]==s[j] and dp[j+1]==1:
dp[j] = 1
cnt += 1
else:
dp[j]=0
return cnt
3、最长回文子串
题目描述:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
思路分析:跟第二题相似,不过这次我们需要记录当前最长的回文子串,因此我们只要在没找到一个回文子串的时候比较一下当前的长度与之前最大长度,取其中最大值。
代码如下
class Solution:
def longestPalindrome(self, s: str) -> str:
size = len(s)
if size < 2:
return s
dp = [[False for _ in range(size)] for _ in range(size)]
max_len = 1
start = 0
for i in range(size):
dp[i][i] = True
for j in range(1, size):
for i in range(0, j):
if s[i] == s[j]:
if j - i < 3:
dp[i][j] = True
else:
dp[i][j] = dp[i + 1][j - 1]
else:
dp[i][j] = False
if dp[i][j]:
cur_len = j - i + 1
if cur_len > max_len:
max_len = cur_len
start = i
return s[start:start + max_len]
4、最长回文子序列
题目描述:给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。
示例 1:
输入:
“bbbab”
输出:
4
思路分析:最长子序列和最长子串的区别在于,子序列允许中间跳过某些字符,例如bbbab的最长子序列是bbb,而最长子串只能是bbb。
思路与最长子串类似,我们任然定一个二维数组表示索引i到j的最长子序列的长度。
对于dp[i][j]如果s[i]==s[j],
那么dp[i][j]==dp[i+1][j-1]+2 (2是字符是s[i]和s[j])
如果s[i]!=s[j]:
那么dp[i][j]的最长子序列长度只能是i+1到j或者i到j-1.
代码如下
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
len_s = len(s)
dp = [[0] * len_s for _ in range(len_s)]
# base case 每个字符可以是一个回文串
for i in range(len_s):
dp[i][i]=1
for i in range(len_s-1,-1,-1):
for j in range(i+1,len_s):
#长度加2
if s[i]==s[j]:
dp[i][j] = dp[i+1][j-1]+2
else:
dp[i][j]=max(dp[i+1][j],dp[i][j-1])
return dp[0][-1]
5、验证回文串2
题目描述:给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
输入: “abca”
输出: True
解释: 你可以删除c字符。
基本思路:双指针,一个指针从前往后遍历,一个指针从后往前遍历,如果中间碰到两个指针不相等的情况,有两种情况,删除前一个指针所指的字符or删除后一个指针所指字符。此时子字符串为 S[i+1:j+1] 和 S[i:j],再验证这两个字符串是否有一个是回文串。
代码如下:
class Solution:
def validPalindrome(self, s: str) -> bool:
if len(s) == 1:return True
i, j = 0, len(s) - 1
count = 0
while i <= j:
if s[i]==s[j]:
i += 1
j -= 1
else:
s1 = s[i+1:j+1]
s2 = s[i:j]
return s1==s1[::-1] or s2==s2[::-1]
return True
时间复杂度为O(n)。