题目:
给你一个字符串 s,找到 s 中最长的回文子串。
我的方法:
from collections import Counter
class Solution:
def longestPalindrome1(self, s: str) -> str:
# 全部都是重复值
res_counter = list(Counter(s).values())
if (len(res_counter)==1):
return s
s = list(s)
n = len(s)
res_sub = []
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
for i in range(n):
rk = i-1
substring = list()
# 还没有遍历完
while rk + 1 < n:
substring.append(s[rk + 1])
# 全是重复的
length = len(list(Counter(substring).values()))
if length == 1:
if len(substring) > len(res_sub):
res_sub = substring.copy()
#aba形式
elif s[rk + 1] == substring[0]:
if (list(reversed(substring)) == substring):
if len(substring)>len(res_sub):
res_sub = substring.copy()
rk += 1
return "".join(res_sub)
- 一开始想的是,找出重复字符出现的位置,然后判断那一串是否是回文。但是,一个字符可能出现很多次,这个方法不行。
- 后来又想到之前做过的一道,用到了滑动窗口的题目。之前那道题是寻找最长不重复子串,每次没有循环完整个字符串。这道题目需要循环完整个字符串。
- 我的思路是:滑动窗口不停移动,直到找到一个值与substring[0]相同,判断是否是回文。
- 好不容易写出来了,但是说超时了。又试了好几次,还是超时,最后看了看答案,原来要用动态规划。
动态规划
- 基本思想:将待求解的问题分解为若干子问题。能够保存已解决的子问题的答案,在需要时再找出来。这样可以减少运算。
- 经典问题:最短路径问题,背包问题,机器负荷分配问题。
参考答案1:
- 用到了动态规划的思想:当子串长度大于2时,如果
P(i,j)
是回文,那在其左右加上一个相同的数,也是回文。 - 当子串长度为1时,肯定为回文。长度为2时,只要两个字符一样,就是回文了。
官方解释 - 啊,代码创建了动态规划dynamic programing表,表示
s[i][j]
是否为回文。学到了学到了~
class Solution:
def longestPalindrome(self, s: str) -> str:
size = len(s)
# 特殊处理
if size == 1:
return s
# 创建动态规划dynamic programing表
dp = [[False for _ in range(size)] for _ in range(size)]
# 初始长度为1,这样万一不存在回文,就返回第一个值(初始条件设置的时候一定要考虑输出)
max_len = 1
start = 0
for j in range(1,size):
for i in range(j):
# 边界条件:
# 只要头尾相等(s[i]==s[j])就能返回True
if j-i<=2:
if s[i]==s[j]:
dp[i][j] = True
cur_len = j-i+1
# 状态转移方程
# 当前dp[i][j]状态:头尾相等(s[i]==s[j])
# 过去dp[i][j]状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
else:
if s[i]==s[j] and dp[i+1][j-1]:
dp[i][j] = True
cur_len = j-i+1
# 出现回文更新输出
if dp[i][j]:
if cur_len > max_len:
max_len = cur_len
start = i
return s[start:start+max_len]
参考答案 2:
- 本质:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值,即可得到最终的答案。
- 注意一下:回文串的中心可能是一个字符如:aba,此时中心为b。 也可能是两个字符如:abba,此时中心为bb。
- 啊,这个办法好巧妙哦!专门写了一个函数来不停扩展,主函数直接调用就好了。
class Solution:
def expandAroundCenter(self, s, left, right):
# 可以扩展的条件
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return left + 1, right - 1
def longestPalindrome(self, s: str) -> str:
start, end = 0, 0
for i in range(len(s)):
left1, right1 = self.expandAroundCenter(s, i, i)
left2, right2 = self.expandAroundCenter(s, i, i + 1)
if right1 - left1 > end - start:
start, end = left1, right1
if right2 - left2 > end - start:
start, end = left2, right2
return s[start: end + 1]
新知识:
- 判断字符串中每个字符出现次数(返回的是字典):
from collections import Counter
res_counter = Counter("abdc")
- 判断list中重复字符:使用
set()
。 - 将字符串逆序:① 切片法:
print(str[::-1])
② 先转换成list,再使用list.reverse()
③b = reversed(a)
- list转str:
str.join(sequence)
将序列中的元素以指定的字符连接生成一个新的字符串。str表示连接符。、
str = '##'
str.join(['1','2','3'])
>>'1##2##3'
- list赋值尽量不要用
=
,等号赋值类似于C语言中的引用了,原值改变,赋值值也会改变。用copy