1.代码随想录Day62.代码随想录Day7|哈希表part02 3.代码随想录Day8|字符串4.代码随想录Day9| 5.代码随想录Day10|栈与队列6.代码随想录Day11|栈和队列7.代码随想录12|栈和队列8.代码随想录Day14|二叉树9.代码随想录Day15|二叉树10.代码随想录Day15|二叉树III11.代码随想录算法训练营14期 Day112.代码随想录训练营day2| leetcode977, 209,5913.代码随想录day16| 二叉树(四)14.代码随想录Day17|二叉树(五)15.代码随想录Day19|二叉树(六)16.代码随想录|二叉树(最后一章)17.代码随想录|二叉树总结18.代码随想录Day23|回溯算法19.代码随想录Day24|回溯算法+JAVA大作战20.代码随想录|回溯算法(终回)21.代码随想录Day30|贪心122.代码随想录Day32|贪心II23.代码随想录|贪心III24.代码随想录|贪心(终章)25.代码随想录|动态规划26.代码随想录|动态规划-01背包问题27.代码随想录|完全背包28.代码随想录|打家劫舍问题29.代码随想录|各种买卖股票问题30.代码随想录|动态规划 - 子序列系列31.代码随想录|动态规划32.代码随想录|动态规划-编辑距离
33.代码随想录|动态规划(终章)
34.代码随想录|单调栈647. 回文子串
516.最长回文子序列
动态规划总结篇
647. 回文子串
dp[i][j]为[i,j]是否为回文串
这里要注意的是我们的遍历顺序,不能是i一遍,j一遍这样,因为会包含后面的信息
所以我们这里选择的是先遍历长度,然后再遍历首字符
class Solution: def countSubstrings(self, s: str) -> int: n = len(s) dp = [[False for _ in range(n+1)] for _ in range(n+1)] ans = n for i in range(n+1): dp[i][i] = True for l in range(2, n+1): for i in range(n-l+1): if s[i] == s[i+l-1]: if i+1 <= i+l-2: if dp[i+1][i+l-2]: dp[i][i+l-1] = True ans += 1 else: dp[i][i+l-1] = True ans += 1 return ans
参考答案非常机智!!!
首先看这幅图,在我们需要知道dp[i][j]是否为回文子串的前提是知道dp[i+1][j-1]
所以!!!
i是从大到小进行遍历, j是从小到大进行遍历。所以!!非常有意思。
并且在进行判断的时候,使用i-j<=1 然后进行dp[i][i+l-1] = True,这样的话可读性更强
class Solution: def countSubstrings(self, s: str) -> int: dp = [[False] * len(s) for _ in range(len(s))] result = 0 for i in range(len(s)-1, -1, -1): #注意遍历顺序 for j in range(i, len(s)): if s[i] == s[j] and (j - i <= 1 or dp[i+1][j-1]): result += 1 dp[i][j] = True return result
双指针法
动态规划的空间复杂度是偏高的,我们再看一下双指针法。
首先确定回文串,就是找中心然后向两边扩散看是不是对称的就可以了。
在遍历中心点的时候,要注意中心点有两种情况。
一个元素可以作为中心点,两个元素也可以作为中心点。
那么有人同学问了,三个元素还可以做中心点呢。其实三个元素就可以由一个元素左右添加元素得到,四个元素则可以由两个元素左右添加元素得到。
所以我们在计算的时候,要注意一个元素为中心点和两个元素为中心点的情况。
class Solution: def countSubstrings(self, s: str) -> int: result = 0 for i in range(len(s)): result += self.extend(s, i, i, len(s)) #以i为中心 result += self.extend(s, i, i+1, len(s)) #以i和i+1为中心 return result def extend(self, s, i, j, n): res = 0 while i >= 0 and j < n and s[i] == s[j]: i -= 1 j += 1 res += 1 return res
516.最长回文子序列
dp[i][j]的定义是[i,j]最长的回文序列
if s[i] == s[j] : dp[i][j] = dp[i+1][j-1]+2
if s[i] != s[j]: dp[i][j] = max(dp[i+1][j], dp[i][j-1])
一个从正面,一个反面
j > i
class Solution: def longestPalindromeSubseq(self, s: str) -> int: 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][-1]
动态规划最强总结篇!
动规五部曲分别为:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
动规专题刚开始的时候,讲的题目比较简单,不少录友和我反应:这么简单的题目 讲的复杂了,不用那么多步骤分析,想出递推公式直接就AC这道题目了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!