LeetCode 刷题小本本Day6 Longest Palindromic Substring(动态规划)

题目:

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

我的方法:

from collections import Counter
class Solution:
    def longestPalindrome1(self, s: str) -> str:
        # 全部都是重复值
        res_counter = list(Counter(s).values())
        if (len(res_counter)==1):
            return s
        s = list(s)
        n = len(s)
        res_sub = []
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        for i in range(n):
            rk = i-1
            substring = list()
            # 还没有遍历完
            while rk + 1 < n:
                substring.append(s[rk + 1])
                # 全是重复的
                length = len(list(Counter(substring).values()))
                if length == 1:
                    if len(substring) > len(res_sub):
                        res_sub = substring.copy()
                #aba形式
                elif s[rk + 1] == substring[0]:
                    if (list(reversed(substring)) == substring):
                        if len(substring)>len(res_sub):
                            res_sub = substring.copy()
                rk += 1
        return "".join(res_sub)
  1. 一开始想的是,找出重复字符出现的位置,然后判断那一串是否是回文。但是,一个字符可能出现很多次,这个方法不行。
  2. 后来又想到之前做过的一道,用到了滑动窗口的题目。之前那道题是寻找最长不重复子串,每次没有循环完整个字符串。这道题目需要循环完整个字符串。
  3. 我的思路是:滑动窗口不停移动,直到找到一个值与substring[0]相同,判断是否是回文。
  4. 好不容易写出来了,但是说超时了。又试了好几次,还是超时,最后看了看答案,原来要用动态规划。

动态规划

  1. 基本思想:将待求解的问题分解为若干子问题。能够保存已解决的子问题的答案,在需要时再找出来。这样可以减少运算。
  2. 经典问题:最短路径问题,背包问题,机器负荷分配问题。

参考答案1:

  1. 用到了动态规划的思想:当子串长度大于2时,如果P(i,j)是回文,那在其左右加上一个相同的数,也是回文。
  2. 当子串长度为1时,肯定为回文。长度为2时,只要两个字符一样,就是回文了。
    官方解释
  3. 啊,代码创建了动态规划dynamic programing表,表示s[i][j]是否为回文。学到了学到了~
class Solution:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        # 特殊处理
        if size == 1:
            return s
        # 创建动态规划dynamic programing表
        dp = [[False for _ in range(size)] for _ in range(size)]
        # 初始长度为1,这样万一不存在回文,就返回第一个值(初始条件设置的时候一定要考虑输出)
        max_len = 1
        start = 0 
        for j in range(1,size):
            for i in range(j):
                # 边界条件:
                # 只要头尾相等(s[i]==s[j])就能返回True
                if j-i<=2:
                    if s[i]==s[j]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 状态转移方程 
                # 当前dp[i][j]状态:头尾相等(s[i]==s[j])
                # 过去dp[i][j]状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
                else:
                    if s[i]==s[j] and dp[i+1][j-1]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 出现回文更新输出
                if dp[i][j]:
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i

        return s[start:start+max_len]

参考答案 2:

  1. 本质:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值,即可得到最终的答案。
  2. 注意一下:回文串的中心可能是一个字符如:aba,此时中心为b。 也可能是两个字符如:abba,此时中心为bb。
  3. 啊,这个办法好巧妙哦!专门写了一个函数来不停扩展,主函数直接调用就好了。
class Solution:
    def expandAroundCenter(self, s, left, right):
    # 可以扩展的条件
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    def longestPalindrome(self, s: str) -> str:
        start, end = 0, 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)
            left2, right2 = self.expandAroundCenter(s, i, i + 1)
            if right1 - left1 > end - start:
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start: end + 1]

新知识:

  1. 判断字符串中每个字符出现次数(返回的是字典):
from collections import Counter
res_counter = Counter("abdc")
  1. 判断list中重复字符:使用set()
  2. 将字符串逆序:① 切片法:print(str[::-1]) ② 先转换成list,再使用list.reverse()b = reversed(a)
  3. list转str:str.join(sequence)将序列中的元素以指定的字符连接生成一个新的字符串。str表示连接符。、
str = '##'
str.join(['1','2','3'])
>>'1##2##3'
  1. list赋值尽量不要用=,等号赋值类似于C语言中的引用了,原值改变,赋值值也会改变。用copy
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值