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。
打卡第五天!虽然有点晚,还是坚持下来了!加油!