1、最长回文子串
这道题要好好说一下,做了好几次了,做完过一段时间又忘了,所以总结一下,以后忘得时候拿出来看,用两种比较好理解的方法来做
先上动态规划的代码
class Solution:
def longestPalindrome(self, s: str) -> str:
if not s:
return ''
n = len(s)
maxlen = 0
res = 0
dp = [[False for _ in range(n)]for _ in range(n)]
for j in range(n):
for i in range(j+1):
dp[i][j] = s[i]==s[j] and (j-i<=2 or dp[i+1][j-1])
if dp[i][j]:
if j-i+1>maxlen:
maxlen = j-i+1
res = s[i:j+1]
return res
以s = ' babad' 为例,j不断地向后,i是从0到j,不断地去判断,所以两层for循环是写成代码里那样,当j=4时,i从0到4,然后去查找最长回文
当然,两层for循环还有其他的写法,图片来自来源
dp[i][j] 表示的是 s[i] 和 s[j] 是否相等,如果相等,并且 dp[i+1][j-1] 也相等,说明之前是回文子串,然后再加上现在这两个。所以dp[i][j] = s[j] == s[j] && dp[i+1][j-1],但是看代码发现还有 j - i <=2 这个选项,原因如下:
s = ' babad' ,当 i =0 ,j=2的时候,j-i=2,并且 s[j] == s[j],中间只剩一个元素a肯定是回文,所以就不需要判断 dp[i+1][j-1],直接就是对的。
而且 j - i <= 2 要写在 dp[i+1][j-1]前面,否则会报错,因为当 i=1,j=2时,如果先写 dp[i+1][j-1] ,那么i 变成2,j变成1,这样从刚才的分析来说是不对的,因为j是不断地向后,而i是从0到 j的,i 就超出 j了,所以是不行的。其他的部分比较好懂就不解释了
第二种方法:
class Solution:
def __init__(self):
self.res = ''
def longestPalindrome(self, s: str) -> str:
if not s:
return ''
for i in range(len(s)):
self.helper(s,i,i)
self.helper(s,i,i+1)
return self.res
def helper(self,s,left,right):
while left>=0 and right<len(s) and s[left]==s[right]:
left-=1
right+=1
cur = s[left+1:right]
if len(cur)>len(self.res):
self.res = cur
这种方法采取的是中心扩散法
从中间向两边扩散,但是字符串的长度可能是奇数,可能是偶数,所以直接写两次,结果是一样的
2.最长回文子序列
先上代码
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
if not s:
return 0
n = len(s)
dp = [[0 for _ in range(n)]for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(n-1,-1,-1):
for j in range(i+1,n):
if s[i]==s[j]:
dp[i][j] = dp[i+1][j-1]+2
else:
dp[i][j] = max(dp[i][j-1],dp[i+1][j])
return dp[0][n-1]
核心代码是下文,其实还比较好理解,如果两端的字母相同,那长度加2,否则就看两边子串的长度,取最长的那个
if s[i]==s[j]:
dp[i][j] = dp[i+1][j-1]+2
else:
dp[i][j] = max(dp[i][j-1],dp[i+1][j])
对于两个for循环,第一个for循环是从后面往前来设置i的位置,假如字符串是 "bbbab" ,当 i 指向第2个位置的时候,j从第3个开始依次向后和第i个位置的字母进行比较