day 44 代码随想录 | 回文串个数 最长回文字串 最长回文子序列

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]
        
            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值