来自北大算法课的Leetcode题解:5. 最长回文子串

代码仓库Github | Leetcode solutions @doubleZ0108 from Peking University.

  • 解法1(T5% S99%): 双指针法,左指针依次迭代,每次从最右边一个一个比较,如果相等二者都往中间走一步,如果某一次不相等,左指针归位,右指针从最开始的前一个重新来过

    • 要注意的是某次匹配上了,之后有可能会错,所以要加flag
    • 改进1(T20% S97%): 右指针每次匹配错了要回到右边时如果比当前最好结果还短就不用看了
  • 解法2(T87% S70%): 中心扩散,每个位置往左右两边扩散,找到最长的那个

    • 要注意 a aa aaa这种都是对称的,因此初始化right时应用一个while一直把相等的都算做初始的中心串
  • 解法3(T40% S28%): 动态规划

    dp[i][j]代表从i到j的子串是否为回文串

    转移方程:如果里面的子串是回文,并且把头的两个还相等,则可以往左右各扩展一位

    d p [ i ] [ j ] = { T r u e , i f   d p [ i + 1 ] [ j − 1 ]   a n d   s [ i ] = = s [ j ] F a l s e , e l s e dp[i][j] = \begin{cases} True, & if \ dp[i+1][j-1] \ and \ s[i]==s[j] \\ False, & else \end{cases} dp[i][j]={True,False,if dp[i+1][j1] and s[i]==s[j]else

    边界条件:一个字符肯定是回文,两个相邻字符如果相同则为回文

    d p [ i ] [ i ] = T r u e ( d p [ i ] [ i + 1 ]   a n d   s [ i ] = = s [ i + 1 ] ) = T r u e dp[i][i] = True \\ (dp[i][i+1] \ and \ s[i]==s[i+1]) = True dp[i][i]=True(dp[i][i+1] and s[i]==s[i+1])=True

    需要注意,因为dp是不断往i+1和j-1方向扩展的,因此要首先从右下角开始扩展,同时由于对称结构j从i的下一位开始就好了

    不需要把整个dp数组先建立完再两重循环找,做完dp[i][j]就可以直接进行判断了

    dp = [[False]*len(s) for _ in range(len(s))]
    for i in range(len(s)):
        dp[i][i] = True
        if i+1<len(s) and s[i]==s[i+1]:
            dp[i][i+1] = True
    
    for i in range(len(s)-1, -1, -1):
    	  for j in range(i, len(s)):
    	      if i+1<len(s) and j>-1 and dp[i+1][j-1] and s[i]==s[j]:
    	          dp[i][j] = True
    	
    	      if dp[i][j] and j-i+1>len(ans):
    	          ans = s[i:j+1]
    
  • 解法4(过于复杂 没尝试): Manacher马拉车,插入不会出现的值进行分割,用更好的初始值减小回文的搜索

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        # 解法2
        ans = s[0]
        for i in range(len(s)):
            center = i
            left = i 
            right = i
            while right+1<len(s) and s[right+1]==s[right]:
                right += 1

            while left>-1 and right<len(s) and s[left]==s[right]:
                left -= 1
                right += 1

            if len(s[left+1:right]) > len(ans):
                ans = s[left+1:right]

        return ans

    
    def otherSolution(self, s):
        # 解法3
        ans = s[0]

        dp = [[False]*len(s) for _ in range(len(s))]
        for i in range(len(s)):
            dp[i][i] = True
            if i+1<len(s) and s[i]==s[i+1]:
                dp[i][i+1] = True 
        
        for i in range(len(s)-1, -1, -1):
            for j in range(i, len(s)):
                if i+1<len(s) and j>-1 and dp[i+1][j-1] and s[i]==s[j]:
                    dp[i][j] = True

                if dp[i][j] and j-i+1>len(ans):
                    ans = s[i:j+1]
        
        return ans


        # 解法1
        if len(s) == 1:
            return s

        ans = s[0]
        for i in range(len(s)):
            left, right = i, len(s)-1
            start = left
            has = False
            if right-left+1 < len(ans):
                break

            while left < right:
                if s[left] == s[right]:
                    if not has:     # 第一次匹配上记录又端点
                        has = True
                        end = right
                    left += 1
                    right -= 1
                else:
                    if has:         # 如果之前匹配上了但中间有不匹配的
                        has = False
                        left = start
                        right = end-1
                        # 改进1
                        if right-left+1 < len(ans):
                            break
                    else:
                        right -= 1

            if has and len(ans) < end-start+1:
                ans = s[start:end+1]
        
        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

doubleZ0108

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值