day46 动态规划-回文子串

115 阅读7分钟

647. 回文子串

文章讲解

思路1:双指针

使用中心扩展法,从中间往两边扩展,看看能不能成为回文子串。
class Solution:
    def countSubstrings(self, s: str) -> int:
        # 遍历所有的中心,尝试往外扩展找到答案;要考虑奇数和偶数的情况
        res = 0
        for i in range(len(s)):
            res += self.expandAroundCenter(s, i, i)
            res += self.expandAroundCenter(s, i, i+1)
        return res

    def expandAroundCenter(self, s: str , left: int, right: int)->int:
        # 中心扩展法:沿中心往两边扩展,找到回文子串的数量
        count = 0
        while left >= 0 and right < len(s) and s[left] == s[right]:
             count += 1
             left -= 1
             right += 1
        return count

另:

来自gpt的生动解释:

**题目是什么意思?**

想象你有一串珠子,每个珠子上有一个字母。题目要你找出这串珠子里有多少“回文珠子串”。

什么是“回文珠子串”呢? 就是正着念和倒着念都一样的珠子串。

-   比如 “aba” 就是回文的,因为正着念是 “aba”,倒着念也是 “aba”。
-   “abc” 就不是回文的,因为正着念是 “abc”,倒着念是 “cba”。

题目要你数出,一串珠子里,有多少个这样的回文珠子串。

**例子解释**

-   **例子1: “abc”**

    -   “a” 是回文的
    -   “b” 是回文的
    -   “c” 是回文的
    -   所以总共有 3 个回文珠子串。

-   **例子2: “aaa”**

    -   “a” 是回文的 (有3个)
    -   “aa” 是回文的 (有2个)
    -   “aaa” 是回文的 (有1个)
    -   所以总共有 3 + 2 + 1 = 6 个回文珠子串。

**怎么解决这个问题?**

我们可以用一种叫做“中心扩展法”的方法来解决。 想象你有一根魔法棒,你可以把魔法棒放在珠子串的中间,然后向两边扩展,看看能不能变成回文串。

1.  **选择中心:**  我们从每个珠子开始,把它当做回文串的中心。

2.  **向两边扩展:**  从中心珠子开始,向左边和右边扩展。

    -   如果左右两边的珠子一样,那么我们就找到了一个更长的回文串!
    -   如果左右两边的珠子不一样,或者已经到了珠子串的边缘,那么就不能再扩展了。

**举个例子: 字符串 “aba”**

1.  **中心是 ‘a’ (第一个):**

    -   “a” 是回文的 (长度 1)
    -   向两边扩展,没有珠子了,结束。

2.  **中心是 ‘b’:**

    -   “b” 是回文的 (长度 1)
    -   向两边扩展,左边是 ‘a’,右边是 ‘a’,一样!
    -   “aba” 是回文的 (长度 3)
    -   继续向两边扩展,没有珠子了,结束。

3.  **中心是 ‘a’ (第三个):**

    -   “a” 是回文的 (长度 1)
    -   向两边扩展,没有珠子了,结束。

所以,“aba” 中有 3 个回文子串: “a”, “b”, “a”, “aba”。

516. 最长回文子序列

文章讲解

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

思路:

思路:回文子序列可以跳过某些元素,例如abca,包含aa这个回文子序列,长度为2。
定义:dp[i][j]表示s从i到j的子串中的最长回文子序列。
初始化:i=j的时候,单个字符就是回文子序列,=1
递推:考虑两种情况 s[i]==s[j]或者不相等,如果相等的话就是中间的+2;如果不相等,就尝试删除s[i]或者s[j],取最大。
    s[i]==s[j]  dp[i][j] = dp[i+1][j-1] + 2  # 例如aba,b为中心的基础上+2
    s[i]!=s[j]  dp[i][j] = max(dp[i+1][j], dp[i][j-1]) # 例如 abb,虽然两侧a!=b,但是右边两个长度+1
迭代方向:注意不是从前到后了!,i从后往前,j从i+1到n+1 
class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0] * 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+1][j], dp[i][j+1])   # j-1啊!!!
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1])
        return dp[0][n-1] # 开头到结尾整个字符串的最大回文子序列

gpt思路:

还是想象你有一串珠子,每个珠子上有一个字母。 这次,题目不是要你找连续的回文珠子串,而是要你找“最长回文珠子序列”。

什么是“珠子序列”呢? 就是从一串珠子里挑出一些珠子,按照它们原来的顺序排列。 你可以跳过一些珠子,也可以不跳过任何珠子。

什么是“回文珠子序列”呢? 就是正着念和倒着念都一样的珠子序列。

题目要你找出,一串珠子里,最长的回文珠子序列有多长。

**例子解释**

-   **例子1: “bbbab”**

    -   “bbbb” 是一个回文珠子序列 (跳过了中间的 ‘a’)
    -   “bbbab” 本身也是一个回文珠子序列 (没有跳过任何珠子)
    -   最长的回文珠子序列是 “bbbb”,长度是 4。

-   **例子2: “cbbd”**

    -   “bb” 是一个回文珠子序列 (跳过了 ‘c’ 和 ‘d’)
    -   “c” 和 “d” 也可以是长度为 1 的回文珠子序列
    -   最长的回文珠子序列是 “bb”,长度是 2。

**怎么解决这个问题?**

我们可以用一种叫做“动态规划”的方法来解决。 动态规划就像玩积木一样,先搭好小的积木,然后用小的积木搭更大的积木,最后搭成我们想要的城堡。

1.  **定义状态:**  我们用一个表格 `dp[i][j]` 来表示:从第 `i` 个珠子到第 `j` 个珠子之间,最长回文珠子序列的长度。

1.  **初始化:**  如果只有一个珠子 (i == j),那么它本身就是一个回文珠子序列,长度是 1。 所以 `dp[i][i] = 1`1.  **状态转移:**  我们要考虑两种情况:

    -   **情况1:**  如果第 `i` 个珠子和第 `j` 个珠子一样,那么我们可以把它们加到回文珠子序列里,所以 `dp[i][j] = dp[i+1][j-1] + 2`。 (就像在两边加上相同的珠子,让回文序列更长)
    -   **情况2:**  如果第 `i` 个珠子和第 `j` 个珠子不一样,那么我们只能选择要么不要第 `i` 个珠子,要么不要第 `j` 个珠子,选择能让回文珠子序列更长的那个。 所以 `dp[i][j] = max(dp[i+1][j], dp[i][j-1])`1.  **结果:**  最后,`dp[0][n-1]` 就是从第一个珠子到最后一个珠子之间,最长回文珠子序列的长度,也就是我们想要的答案。

**举个例子: 字符串 “bbbab”**

我们来填一个表格:

复制代码 b b b a b --------------------- b | 1 2 3 3 4 b | 1 2 2 3 b | 1 1 3 a | 1 1 b | 1


-   `dp[0][0] = 1` (第一个 ‘b’ 到第一个 ‘b’,最长回文序列是 “b”)
-   `dp[1][1] = 1` (第二个 ‘b’ 到第二个 ‘b’,最长回文序列是 “b”)
-   `dp[2][2] = 1` (第三个 ‘b’ 到第三个 ‘b’,最长回文序列是 “b”)
-   `dp[3][3] = 1` ( ‘a’ 到 ‘a’,最长回文序列是 “a”)
-   `dp[4][4] = 1` (最后一个 ‘b’ 到最后一个 ‘b’,最长回文序列是 “b”)
-   `dp[0][1] = 2` (第一个 ‘b’ 到第二个 ‘b’,最长回文序列是 “bb”)
-   `dp[1][2] = 2` (第二个 ‘b’ 到第三个 ‘b’,最长回文序列是 “bb”)
-   `dp[2][3] = 1` (第三个 ‘b’ 到 ‘a’,最长回文序列是 “b” 或者 “a”)
-   `dp[3][4] = 1` ( ‘a’ 到最后一个 ‘b’,最长回文序列是 “a” 或者 “b”)
-   `dp[0][2] = 3` (第一个 ‘b’ 到第三个 ‘b’,最长回文序列是 “bbb”)
-   `dp[1][3] = 2` (第二个 ‘b’ 到 ‘a’,最长回文序列是 “bb”)
-   `dp[2][4] = 3` (第三个 ‘b’ 到最后一个 ‘b’,最长回文序列是 “bbb”)
-   `dp[0][3] = 3` (第一个 ‘b’ 到 ‘a’,最长回文序列是 “bbb”)
-   `dp[1][4] = 3` (第二个 ‘b’ 到最后一个 ‘b’,最长回文序列是 “bbb”)
-   `dp[0][4] = 4` (第一个 ‘b’ 到最后一个 ‘b’,最长回文序列是 “bbbb”)

所以,最长回文珠子序列的长度是 4

打卡第五天!虽然有点晚,还是坚持下来了!加油!