【Leetcode5】最长回文子串

题目描述

在这里插入图片描述

解题思路

回文数:回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。(第1个数和第n个数相等,第2个数和第n-1个数相等…)

解法一:暴力法(超时)

python代码

  • 枚举所有长度大于等于 2的子串,依次判断它们是否是回文;
  • ‘babad’,判断‘ba’,‘bab’,‘baba’,‘babad’,‘ab’,‘aba’,‘abad’,‘ba’,‘bad’,‘ad’是否符合回文数
  • 找到最长的回文子串,例如‘ccc’,最长回文子串为‘ccc’,则需加上判断j-i+1>max_len
class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n<2:
            return s
        res = s[0]
        max_len = 1
        for i in range(0,n-1):
            for j in range(i+1,n):
                if j-i+1>max_len and self.valid(s,i,j):
                    max_len = j-i+1
                    res = s[i:j+1]
        return res

    def valid(self,s,m,n):
        while m<n:
            if s[m] != s[n]:
                return False
            else:
                m += 1
                n -=1
        return True

复杂度分析
时间复杂度:O(n^3),这里n是字符串的长度,枚举字符串的左边界、右边界,然后继续验证子串是否是回文子串,这三种操作都与n相关
空间复杂度:O(1),只使用到常数个临时变量

解法二:动态规划

1、如果一个字符串的头尾两个字符都不相等,那么这个字符串一定不是回文串;
2、如果一个字符串的头尾两个字符相等,才有必要继续判断下去。
(1)如果里面的子串是回文,整体就是回文串;
(2)如果里面的子串不是回文串,整体就不是回文串。
了解动态规划:
1、思考状态
dp[i][j] 表示子串 s[i, j] 是否为回文子串。
2、思考状态转移方程(核心、难点)
这一步在做分类讨论(根据头尾字符是否相等),根据上面的分析得到:
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]
分析这个状态转移方程:
(1)“动态规划”事实上是在填一张二维表格,i 和 j 的关系是 i <= j ,因此,只需要填这张表的上半部分;
在这里插入图片描述
(2)看到 dp[i + 1][j - 1] 就得考虑边界情况。
边界条件是:表达式 [i + 1, j - 1] 不构成区间,即长度严格小于 2,即 j - 1 - (i + 1) + 1 < 2 ,整理得 j - i < 3。
这个结论很显然:当子串 s[i, j] 的长度等于 2 或者等于 3 的时候,我其实只需要判断一下头尾两个字符是否相等就可以直接下结论了。
如果子串 s[i + 1, j - 1] 只有 1 个字符,即去掉两头,剩下中间部分只有1个字符,当然是回文;
如果子串 s[i + 1, j - 1] 为空串,那么子串 s[i, j] 一定是回文子串。
因此,在 s[i] == s[j] 成立和 j - i < 3 的前提下,直接可以下结论,dp[i][j] = true,否则才执行状态转移。
3、思考初始化
初始化的时候,单个字符一定是回文串,因此把对角线先初始化为 1,即 dp[i][i] = 1
4、思考输出
只要一得到 dp[i][j] = true,就记录子串的长度和起始位置,没有必要截取,因为截取字符串也要消耗性能,记录此时的回文子串的“起始位置”和“回文长度”即可。
5、思考状态压缩
因为在填表的过程中,只参考了左下方的数值。事实上可以压缩,但会增加一些判断语句,增加代码编写和理解的难度,丢失可读性。在这里不做状态压缩。

python代码

class Solution:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        if size < 2:
            return s

        dp = [[False for _ in range(size)] for _ in range(size)]

        max_len = 1
        start = 0

        for i in range(size):
            dp[i][i] = True

        for j in range(1, size):
            for i in range(0, j):
                if s[i] == s[j]:
                    if j - i < 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i + 1][j - 1]
                else:
                    dp[i][j] = False

                if dp[i][j]:
                    cur_len = j - i + 1
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i
        return s[start:start + max_len]

复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n^2)二维 dp 问题,一个状态得用二维有序数对表示,因此空间复杂度是 O(n^2)

发布了56 篇原创文章 · 获赞 1 · 访问量 1680

猜你喜欢

转载自blog.csdn.net/weixin_44549556/article/details/105124904