LeetCode5&647回文子串(动态规划)(Python带测试用例)

LeetCode5.最长回文子串

一、题目

给你一个字符串 s,找到 s 中最长的回文子串。

二、思路

1.动态规划概要

动态规划法:把一个复杂问题分解为若干个相互重叠的子问题。
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。

2.解题思路

1.创建动态规划表dp (n x n的全为False的矩阵) ,用True和False来表示字符串s的每一个子串是否为回文。
2.双循环则进行遍历。
3.分情况讨论
(1) 边缘情况1: 子串长度为1, 则一定是回文
(2) 边缘情况2: 子串长度为2, 如果俩字符相同则是回文
(3) 非边缘情况则进行动态规划之状态转移: 如果b是回文, aba也一定是回文。
即判断是否为回文需同时满足两条件:

  1. 剥离左右最外层字符后的子字符串是回文
  2. 最外层的字符相同
    在这里插入图片描述

三、题解

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)

        # 特解,为空字符串和长度为的字符串时,直接返回该字符串
        if n < 2:
            return s

        '''
        创建动态规划表dp (n x n的全为False的矩阵) ,用True和False来表示字符串s的每一个子串是否为回文
        s子串个数 = n + n-1 + ... + 1, e.g. 字符串babad有5 + 4 + 3 + 2 + 1 = 15种子串
        start用来跟踪最长回文子串的起点, max_len代表最长回文子串的长度
        '''
        dp = [[False] * n for _ in range(n)] # dp为n个其中都为n个False组成的列表
        """
        相当于:
        dp = []
        for i in range(n):
            list = []
            for j in range(n):
                list.append(False)
            dp.append(list)
        """
        start, max_len = 0, 1

        '''
        如果字符串是"babad"
        下面的双循环则进行以下遍历 (遍历所有可能的15个子字符串):
        b
        ba,    a
        bab,   ab,   b,
        baba,  abd,  ba,  a
        babad, abad, bda, ad, d

        dp[left][right]代表s被left和right指针相夹而得的子字符串
        e.g. left = 0, right = 3, dp[left][right] = baba
        '''
        for right in range(n):
            for left in range(0, right + 1):
                '''
                (0) 求子串跨越长度span
                (1) 边缘情况1: 子串长度为1, 则一定是回文
                (2) 边缘情况2: 子串长度为2, 如果俩字符相同则是回文
                (3) 非边缘情况则进行动态规划之状态转移: 如果b是回文, aba也一定是回文
                    即判断是否为回文需同时满足两条件: 
                        1. 剥离左右最外层字符后的子字符串是回文 
                        2. 最外层的字符相同
                '''
                span = right - left + 1
                if span == 1:
                    dp[left][right] = True
                elif span == 2:
                    dp[left][right] = s[left] == s[right]
                else:
                    dp[left][right] = dp[left + 1][right-1] and s[left] == s[right]

                # 若新的回文出现, 判断是否需要更新最大长度
                if dp[left][right]:
                    if span > max_len:
                        max_len = span
                        start = left
        # 返回最长回文子串
        return s[start:start + max_len]

l = Solution()
print(l.longestPalindrome("babad")) # bab
print(l.longestPalindrome("cbbd")) # bb
print(l.longestPalindrome("a")) # a
print(l.longestPalindrome("ac")) # a
print(l.longestPalindrome("aaaa")) # aaaa

时间复杂度:O(n^2)
其中 n 是字符串的长度。动态规划的状态总数为 O(n^2)
对于每个状态,我们需要转移的时间为O(1)。

空间复杂度:O(n^2),即存储动态规划状态需要的空间。

LeetCode647.Palindromic Substrings回文子串

一、题目

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

二、题解

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        # 记录回文子串数量
        ans = 0
        '''
        创建动态规划表dp (n x n的全为False的矩阵) ,用True和False来表示字符串s的每一个子串是否为回文
        s子串个数 = n + n-1 + ... + 1, e.g. 字符串babad有5 + 4 + 3 + 2 + 1 = 15种子串
        '''
        dp = [[False] * n for _ in range(n)]  # dp为n个其中都为n个False组成的列表
        """
        相当于:
        dp = []
        for i in range(n):
            list = []
            for j in range(n):
                list.append(False)
            dp.append(list)
        """

        '''
        如果字符串是"babad"
        下面的双循环则进行以下遍历 (遍历所有可能的15个子字符串):
        b
        ba,    a
        bab,   ab,   b,
        baba,  abd,  ba,  a
        babad, abad, bda, ad, d

        dp[left][right]代表s被left和right指针相夹而得的子字符串
        e.g. left = 0, right = 3, dp[left][right] = baba
        '''
        for right in range(n):
            for left in range(0, right + 1):
                '''
                (0) 求子串跨越长度span
                (1) 边缘情况1: 子串长度为1, 则一定是回文
                (2) 边缘情况2: 子串长度为2, 如果俩字符相同则是回文
                (3) 非边缘情况则进行动态规划之状态转移: 如果b是回文, aba也一定是回文
                    即判断是否为回文需同时满足两条件: 
                        1. 剥离左右最外层字符后的子字符串是回文 
                        2. 最外层的字符相同
                '''
                span = right - left + 1
                if span == 1:
                    dp[left][right] = True
                elif span == 2:
                    dp[left][right] = s[left] == s[right]
                else:
                    dp[left][right] = dp[left + 1][right - 1] and s[left] == s[right]

                # 动态规划表为True,为回文子串,ans+1
                if dp[left][right]:
                    ans += 1
        # 返回回文子串数量
        return ans

l = Solution()
print(l.countSubstrings("babad")) # 7
print(l.countSubstrings("abc")) # 3
print(l.countSubstrings("aaa")) # 6
print(l.countSubstrings("cbbd")) # 5
print(l.countSubstrings("a")) # 1
print(l.countSubstrings("ac")) # 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值