回文系列的也是困扰了我很久,这里就做一个关于这个系列代码题的总结。
段式回文
(困难) “axyza”->(a)(xyz)(a)
验证回文字符串 II
给出串可以删一次,判断删完之后是否回文
1.字符串中回文子串的数量
dp定义:区间范围[i,j] (左闭右闭)的子串是否是回文子串
s = "aaa"
result = 0
dp = [[False] * len(s) for _ in range(len(s))]
for i in range(len(s)-1, -1, -1): #注意遍历顺序 "aaa"———>6
for j in range(i, len(s)):# 区间范围[i,j] (左闭右闭)的子串是否是回文子串
if s[i] == s[j]:
if j - i <= 1:
result += 1
dp[i][j] = True # 单个字符或成对字符
elif dp[i+1][j-1]:
result += 1
dp[i][j] = True
print(result) # 6
for line in dp:
print(dp)
'''
[[True, True, True], [False, True, True], [False, False, True]]
[[True, True, True], [False, True, True], [False, False, True]]
[[True, True, True], [False, True, True], [False, False, True]]
'''
2.字符串中最长回文子序列长度【长度】【长度】
dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
s = "bbbab"
def longestPalindromeSubseq(s):
dp = [[0] * len(s) for _ in range(len(s))]
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)):
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])
for line in dp:
print(line)
return dp[0][-1]
'''
[1, 2, 3, 3, 4]
[0, 1, 2, 2, 3]
[0, 0, 1, 1, 3]
[0, 0, 0, 1, 1]
[0, 0, 0, 0, 1]
'''
3.字符串中的最长回文子串
第一种,普通解法,通俗易懂。
def longestPalindrome(s):
def extend(start_idx, end_idx, max_start, max_len):
"""
扩展函数,用于得到向左向右同步扩展后的最长回文子串
:param start_idx: 向左扩展的起始位置
:param end_idx: 向右扩展的起始位置
:param max_start: 当前最长回文子串的左指针
:param max_len: 当前最大长度
:return: 当前最长回文子串的起始位置和长度
"""
while 0 <= start_idx <= end_idx < len(s): # 子串起止下标合法时
if s[start_idx] == s[end_idx]: # 如果新增的两端字符相等
cur_str = s[start_idx:end_idx] # 当前子串是回文串
cur_len = end_idx + 1 - start_idx # 当前子串长度
if max_len < cur_len:
max_len = cur_len
max_start = start_idx
start_idx -= 1 # 左指针向左移动一位
end_idx += 1 # 右指针向右移动一位
else:
break
return max_start, max_len
max_start, max_len = 0, 0 # 初始化最长回文子串开始位置及长度
for i in range(len(s)): # 进行一次遍历
left = right = i # 判断长度为奇数的回文子串开始和结束位置
# 从i位置向两边扩展,搜寻可以得到的最大回文子串
max_start, max_len = extend(left, right, max_start, max_len)
left, right = i, i+1 # 判断长度为偶数的回文子串开始和结束位置
# 从i位置向左扩展,从i+1位置向右扩展,搜寻可以得到的最大回文子串
max_start, max_len = extend(left, right, max_start, max_len)
return s[max_start:max_start + max_len]
print(longestPalindrome("babad"))
第二种,动态规划。原理可以看这里。
d[i][j]: s[i:j]=1 如果s_i:s_j 是回文串
def longestPalindrome(s):
longest = 0
len_s = len(s)
dp = [[0 for _ in range(len_s)] for _ in range(len_s)]
sublongeststr = ""
for j in range(0, len_s):
for i in range(0, j + 1):
if j - i <= 1:
if s[i] == s[j]:
dp[i][j] = 1
if longest < j - i + 1:
longest = j - i + 1
sublongeststr = s[i:j + 1]
else:
if j - i > 1:
if s[i] == s[j] and dp[i + 1][j - 1]:
dp[i][j] = 1
if longest < j - i + 1:
longest = j - i + 1
sublongeststr = s[i:j + 1]
for line in dp:
print(line)
return sublongeststr
4.分割回文子串
题目描述:
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
实例:
输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]
代码参考的这里。
def palindromePartitioning(s):
'''
分割回文子串
dp+dfs
:param s: str
:return: list[list[str]]
'''
final = []
lens = len(s)
dp = [[0 for _ in range(0, lens)] for _ in range(lens)]
for j in range(0, lens):
for i in range(0, j + 1):
if j - i <= 1:
if s[i] == s[j]:
dp[i][j] = 1
if j - i > 1:
if s[i] == s[j] and dp[i + 1][j - 1] == 1:
dp[i][j] = 1
for line in dp:
print(line)
def helper(i, tmp):
if i == lens:
final.append(tmp)
for j in range(i, lens):
if dp[i][j] == 1:
print(i, ",", j + 1)
print(s[i:j + 1])
helper(j + 1, tmp + [s[i:j + 1]])
helper(0, [])
return final
print(palindromePartitioning("google"))
# d[i][j]: s[i:j]=1 如果s_i:s_j 是回文串
'''
g o o g l e
g[1, 0, 0, 1, 0, 0]
o[0, 1, 1, 0, 0, 0]
o[0, 0, 1, 0, 0, 0]
g[0, 0, 0, 1, 0, 0]
l[0, 0, 0, 0, 1, 0]
e[0, 0, 0, 0, 0, 1]
0 , 1 g
1 , 2 o
2 , 3 o
3 , 4 g
4 , 5 l
5 , 6 e
1 , 3 oo
3 , 4 g
4 , 5 l
5 , 6 e
0 , 4 goog
4 , 5 l
5 , 6 e
[['g', 'o', 'o', 'g', 'l', 'e'], ['g', 'oo', 'g', 'l', 'e'], ['goog', 'l', 'e']]
'''
0.验证回文串
def func(s):
mid = len(s) // 2
l = mid-1
r = mid+1
while l >= 0 and r < len(s):
if s[l].upper() == s[r].upper():
l -= 1
r += 1
else:
return False
return True # 中心扩散法,从中间往外走
print(func("abcba"))
print(func("abcda"))
def func(s):
left,right = 0,len(s)-1 # 从外往里走
while left < right:
if s[left].isalnum() and s[right].isalnum(): # isalnum判断当前字符是字母或数字
if s[left].upper() == s[right].upper():
left += 1
right -= 1
else:
return False # upper转大写判断 lower
elif not s[left].isalnum():
left += 1
elif not s[right].isalnum():
right -= 1
return True
print(func("abcba"))
print(func("abcda"))