• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
fangleSea
博客园    首页    新随笔    联系   管理    订阅  订阅
代码随想录|动态规划(终章)
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]
        

 


动态规划最强总结篇!

动规五部曲分别为:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

动规专题刚开始的时候,讲的题目比较简单,不少录友和我反应:这么简单的题目 讲的复杂了,不用那么多步骤分析,想出递推公式直接就AC这道题目了。

 

 

posted on 2023-07-06 02:13  跪求个offer  阅读(135)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3