5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:
输入: “babad” 输出: “bab” 注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd” 输出: “bb”

1 [自a] 暴力枚举 ——超时—— 87/103样例

自法1:用list[n] :时间n^2 空间n
自法2:在循环内 边循环边处理记录最大: 时间n^3 空间1

class Solution(object):
    def isHW(self, s):
        n = len(s)
        if n%2==0:
            end = n // 2 - 1
            for i in range(0, end+1):
                if s[i] != s[n-1-i]:
                    return 0
            return 1
        elif n%2==1:
            end = n // 2
            for i in range(0, end+1):
                if s[i] != s[n - 1 - i]:
                    return 0
            return 1

    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        n = len(s)
        strlist = []
        for i in range(n):  # 首字母 最后是s[n-2]
            '''子串 可以是1个字符'''
            str = ""
            for j in range(i,n):
                str += s[j]
                strlist.append(str)

        maxLen = 0
        maxStr = ""
        for str in strlist:
            if self.isHW(str) and len(str)>maxLen:
                maxLen = len(str)
                maxStr = str
        return maxStr

2 中心扩散法 ——微妙优化:空间O1——时复On^2

mh技巧:空间复杂度极好判断
     只要出现list=[3,4,5,3]就是On,
     全程没出现list=[3,4,5,3]就是O1

《 写中心扩散时 领悟的 方法论 》:

1 自己用 debug + 特例 [ 先草纸写值比较 要不必乱 ]
2 出错只关注出错的那部分 只要答题思路对,你就sb一样的只改那一小块即可
     若改蒙蔽了 就新建自己最想要的新标记值调出来
     ‘’‘eg. 比较maxR时候老卡壳r就不用r了,直接用自己最想用的LocalLen简单有清晰!’’’

def longestPalindrome(s):
    n = len(s)

    '''(1)""空串'''
    if n==0:
        return ""

    '''(4)ccc越改越错[因为根本综合不到一起去 数学隔离],直接拎出来处理得了!'''
    ts = s[0]
    v = 1
    while v<n and ts==s[v]:
        '''不长记性! 有while下标 一定先and出下标'''
        v += 1
    if v==n:
        return s

    maxR = -1
    maxLen=0

    for i in range(n):
        '''新技巧:左右多少回文不定,用while!'''
        '''开头左走一个或右走一个 可能左右都成立 可能奇数回文,但咱全都算了 就这么横!'''

        """(2)奇数回文"""
        r1 = 0  # 回文半径
        '''大技巧:涉及whlle配合下标的,一定 先and and后while 确保2哥下标不越界,因为while里面的判断也是普通代码执行会越界'''
        while i-r1 >= 0 and i+r1 < n and s[i-r1] == s[i+r1]:
            r1 += 1
        Len1 = 1 + 2*(r1-1)

        """(3)偶数回文"""
        '''左1'''
        r2 = 0  # 回文半径
        if i-1 >=0 and i<n and s[i-1] == s[i]:  # 注意!! 所有下标都有限制,不要只限制核心下标
            while i-1 -r2 >= 0 and i +r2 < n and s[i-1 -r2] == s[i +r2]:
                r2 += 1
        Len2 = r2*2
        '''右1'''
        r3 = 0
        if i+1 <n and i>=0 and s[i] == s[i+1]:
            while  i+1 +r3 <n and i -r3 >= 0 and s[i -r3] == s[i+1 +r3]:
                r3 += 1
        Len3 = r3 * 2


        '''【成功:信心大增!】2019年8月5日 07点19分:吃完早餐,自己用  debug + 特例[先草纸写值比较 要不必乱!,出错只关注出错的那部分 只要答题思路对,你就sb一样的只改那一小块即可,若思路混乱就新建自己最想要的新标记值,]  调出来,千万别看答案'''
        '''比较maxR时候老卡壳r就不用r了,直接用自己最想用的LocalLen简单有清晰!'''
        if max(Len1,Len2,Len3) > maxLen:
            maxLen = max(Len1,Len2,Len3)
            if maxLen==1:
                r1 -= 1
                '''maxLen=1 只可能是奇数对称'''
                maxStr = s[i - r1: i + r1 + 1]
            else:
                '''mh自创:下面xxx -tmp的格式是mh的精髓,让mh极易理解【草纸上简单标出i i-1 立刻明白下方代码含义】'''
                if Len2 == maxLen:  # 同r 先选偶数的 因为对称中心比奇数 多一个字母
                    '''只关注特殊值的错误处理即可 别想太多 暴力!'''
                    r2 -=1   # 而且偶数tmp要-1 因为第一个有效r的1是对称中心
                    maxStr = s[i-1 -r2 : i +r2+1]
                elif Len3 == maxLen:
                    r3 -= 1
                    maxStr = s[i - r3: i + 1 + r3 + 1]
                elif Len1 == maxLen:
                    r1 -= 1
                    maxStr = s[i -r1 : i +r1+1]

    return maxStr

3 中心扩散的leetcode答案非常精简巧妙,但我能写出上面的方法3,证明我是很nb的 debug方法让我接近无所不能

pass

4 动态规划法 —— 时间n^2 空间 n^2 ———— 虽101/103超时,但方法值得学习

     (4.1) mh自己浅显易懂的原理解释: 从最小的单体开始一点点向大的判断回文,这样大的回文判断就能用上内部的小的dp[][]

     自猜:只要处理好 if ”单b“ elif ”aa“ 这两种情况应该就可以了
     在这里插入图片描述

pass

     (4.2) 力扣答案 的 精妙方法:【答案的作者 也是碰巧想出的 硬碰的】

在这里插入图片描述

def longestPalindrome(s):
    n = len(s)

    if n==0 or n==1:
        return s

    dp = [[False for j in range(n)] for i in range(n)]
    maxLen = 0
    maxStr = ""

    # '''新for的i从0起,随意重复用i!'''
    '''用lr比ij好太多,直观清晰!'''
    '''先分析好数组怎么构造的,因为j可能在i前面for!!'''
    for r in range(1, n):
        for l in range(r):

            '''dp核心:==只判断 1次 边界,内部用之前的dp[][]'''
            if s[l] == s[r]:
                if l+1 == r-1:  #  "aba 不用管最中间的那个b了"
                    dp[l][r] = True
                elif l+1 > r-1:  # "aa"
                    dp[l][r] = True
                elif l+1 < r-1:  # 剩下的为一般普适情况
                    if dp[l+1][r-1] == True:  # 画图后,发现之前的一定准备好了
                        dp[l][r] = True
            elif s[1] != s[r]:
                dp[l][r] = False

            if dp[l][r]==True and r-l > maxLen:
                maxLen = r-l
                maxStr = s[l:r+1]

    """(1)ab maxStr下来是空,应该是a或b"""
    if maxStr == "":
        maxStr = s[0]
        
    return maxStr   
    
    # for tmp in dp:
    #     print(tmp)

5 马拉车算法 ——最强—— O n

原理:收益串更新p[i]简化工作量 + while( i-p[i] i+p[i]) 的所有字符的从p[i]开始的最长回文判断
     注意:之前我只看了i-p[i]一边,其实是为了确保s[对称回文]的左右边界而已
         重要技巧:就是硬约束while条件 不行就debug!

在这里插入图片描述

def longestPalindrome(s):
    n = len(s)

    '''插#号,保证奇数个'''
    new_s = ""
    local = 0
    j = 0  # 分隔#用
    while local <= n-1:  # local记录s中字符已经处理到的 下标
        if j%2 == 0:
            new_s += '#'
        else:
            new_s += s[local]
            local += 1
        j += 1
    '''末尾差个#'''
    new_s += '#'
    new_len = len(new_s)

    max = 0  # 注意:要保证max最右!,收益串的容量大小不看
    mid = 0  # 最大右边界变时,mid才动
    p = []  # p[i]核心数组

    ans_maxNum = 0
    for i in range(new_len):
        """收益串判断 是否可减少工作量"""
        if max > i:
            '''min的作用是保证左方回文串(很大可能经过i-p[i]后扩大到超过mx-i的最大半径。马拉车的收益串最大能用到mx-i的长度)'''
            p.append(min(p[2*mid-i], max-i))
        else:
            p.append(1)  # 开辟空间 要不p的长度是0 后面显示越界
        """收益串判断结束"""
        
        """所有字符都要执行:【0=<i-+p[i]<n】 一定要放后面,因为前面收益串会让你减少工作量,而且i-p[i]每个字符都要扩到最大"""
        while i-p[i]>=0 and i+p[i]<new_len and new_s[i-p[i]]==new_s[i+p[i]]:
            # 用特例 出的 >=0  eg.012 #a#
            p[i] += 1
        
        '''判断右边界max时候可右移'''
        if i+p[i]>max:  # 想第一个p[0],来写代码清晰了就
            max = i + p[i]
            mid = i

        if p[i] > ans_maxNum:
            ans_str = ''  # 一定在for里初始化str ,要不就无限拼接了
            ans_maxNum = p[i]
    
            '''拼接字符串'''
            tmp = p[i]-1
            # 上面用j了 卧槽。。
            ans_str = new_s[i-tmp:i+tmp+1]

    tmpstr = ans_str.replace("#", "")
    return tmpstr
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值