647. 回文子串
- 作为一个回文子串,主打的就是连续子串、两边对称。
- 所以递推公式怎么想呢?子问题dp是一个成型的回文子串,当我左右两边加上相同的字符,才会再次构成当前的回文子串!
【dp数组的定义】:
- 题目是求回文子串的个数。
- 如果直观上用dp的子状态来存储子问题的回文串个数,当i、j位置上元素相等时,那此时确实是子问题的dp值加1,但是没法判断子问题它是不是回文串呀。
- 所以这里的dp设计的是,当前子串是不是回文串,而不是当前子串包含的回文串的
数量。至于最后要求的数量,直接弄个变量进行累加就行了,dp数组都存好了是不是回文串,就不愁数量的计算了。
class Solution:
def countSubstrings(self, s: str) -> int:
size = len(s)
dp = [[0]*size for i in range(size)] #开始时假设任意子串都不是回文的
res = 0
for i in range(size-1, -1, -1): #i从大到小遍历
for j in range(i, size): #j小于i的就不用考虑了,j从小到大遍历
if s[i]==s[j]: #两边相等
if j-i<=1 or dp[i+1][j-1]==1: #长度是1或2,或子问题是回文串
dp[i][j] = 1
res += 1
return res
5. 最长回文子串
和上题本质一样,就是加个变量记录一下最大长度,记录下最大长度的左右边界
class Solution:
def longestPalindrome(self, s: str) -> str:
size = len(s)
dp = [[0]*size for i in range(size)] #开始时假设任意子串都不是回文的
maxLen = 1
l,r = 0,0
for i in range(size-1, -1, -1): #i从大到小遍历
for j in range(i, size): #j小于i的就不用考虑了,j从小到大遍历
if s[i]==s[j]: #两边相等
if j-i<=1 or dp[i+1][j-1]==1: #只有1个或2个字符,或者子问题是回文的
dp[i][j] = 1
if j-i+1>maxLen: #更新最优值
maxLen = j-i+1
l,r = i,j
return s[l:r+1]
516. 最长回文子序列
- 这个题既然要求长度,那我们就让dp存长度值好了啦,一开始默认是0
- 至于中间子问题是不是回文的,我不关心,因为我只考虑长度,即使中间不是回文的,那我只要两边是相等的,这俩就已经构成子序列,就有长度值啦
- 和1143.最长公共子序列那个题类似,只要是子序列就涉及到退步,加个退步就解决了子序列和子串之间的差异。
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
size = len(s)
# dp存储当前子序列最长回文串长度值
dp = [[0]*size for i in range(size)]
for i in range(size-1, -1, -1):
for j in range(i, size):
if s[i]==s[j]: #两边相等
if j-i<=1: #长度只有1或2
dp[i][j] = j-i+1
else:
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][size-1]