647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
- 输入:"abc"
- 输出:3
- 解释:三个回文子串: "a", "b", "c"
示例 2:
- 输入:"aaa"
- 输出:6
- 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
提示:输入的字符串长度不会超过 1000 。
字串是连续的。这个题呢是难在dp数组的定义,如果你定义没有定义好,你递推公式是写不出来的。一般按照dp定义,题目要求什么,我们就定义什么。这个题如果我们按照这个思路,dp[i] 就代表以下表i结尾的字符串有多少个回文串,但是这个题我们没办法找到递归关系,不好推。
dp[i] 和 dp[i+1] dp[i-1] 好像没有关系。因此,会用到回文串的性质。
我们在判断字符串S是否是回文,那么如果我们知道 s[1],s[2],s[3] 这个子串是回文的,那么只需要比较 s[0]和s[4]这两个元素是否相同,如果相同的话,这个字符串s 就是回文串。
因此,我们的dp数组是二维的。布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
2. 递推公式
根据前面的定义,这个递推公式就好推了。
要分为集中情况
如果s[i] != s[j]
那么 dp[i][j] 就不是回文串了
如果s[i] == s[j]
i与j下标相同,那么其实就是同一个字字符 dp[i][j] 为true
i与j下标相差1,比如aa, dp[i][j] 也是true
i与j下标相差大于1,那要看dp[i+1][j-1]是否是回文串,如果是,那他dp[i][j]就是。比如cabac比如i=0 j =4.
3. dp数组初始化
初始化先全为false
4. 确定遍历顺序
这个就有说法了
递推公式中,会从dp[i+1][j-1] 推出
也就是dp[i][j] 的左下方。
因此 遍历 就是从下到上,从左到右。我们是最后推导i=0 j=len(s)-1的
代码
def test(s:str):
dp = [[0] * len(s) for _ in range(len(s)):
res = 0
for i in range(len(s)-1, -1, -1):
for j in range(i, len(s):
if s[i] == s[j]:
if j-i <= 1:
dp[i][j] = 1
res += 1
elif dp[i+1][j-1]:
dp[i][j] = 1
res += 1
return res
5 最长回文字串
给你一个字符串 s
,找到 s
中最长的 回文子串。
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案
这个题其实跟上面那个题如出一辙,稍微改一改就好。
def test(s:str):
dp = [[0] * len(s) for _ in range(len(s))]
start = 0
end = 0
final_len = 0
for i in range(len(s)-1, -1, -1):
for j in range(i, len(s)):
if s[i] == s[j]:
if j-i <= 1:
dp[i][j] = 1
elif dp[i+1][j-1]:
dp[i][j] = 1
if dp[i][j] and j-i + 1 > final_len:
start = i
end = j
final_len = j-i+1
return s[start: end+1]
516.最长回文子序列
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
示例 1: 输入: "bbbab" 输出: 4 一个可能的最长回文子序列为 "bbbb"。
示例 2: 输入:"cbbd" 输出: 2 一个可能的最长回文子序列为 "bb"。
子序列是不要求连续的,因此这里dp定义会有一些不一样。
1. dp数组定义
dp[i][j] 代表下表[i,j]]范围内最长的回文子序列的长度为dp[i][j]
2. dp数组递推公式
分析
如果 s[i] == s[j]
dp[i][j] = dp[i+1][j-1] + 2
如果不相等,那说明 i和j不能 同时加入,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。
dp[i][j] = dp[i+1][j], dp[i][j-1]
3. dp数组初始化
首先要考虑当i 和j 相同的情况,从递推公式:dp[i][j] = dp[i + 1][j - 1] + 2; 可以看出 递推公式是计算不到 i 和j相同时候的情况。因此需要手动初始化,当i与j相同,dp[i][j] =1 是肯定的
其他情况初始化为0就可以
4. 确定遍历顺序
看递推公式
必须要从下往上 从左往右
def test(s: str):
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])
return dp[0][len(s)-1]