Leetcode(5)最长回文子串

Leetcode(4)寻找两个有序数组的中位数

[题目表述]:

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

第一种方法:未完成;利用回文子串的特点

一开始我的思路如下:回文子串的特点是首尾字母相同,所以我对每一个字母都找到位于它后面的相同字母,利用切片判断这一段是否为回文子串(str[i:j]==str[i:j][::-1]).时间复杂度很高,主要是因为str.find操作非常耗时.

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        res=""
        res+=s[0]
        res_star=0
        res_end=0
        Maxsize=1
        for i in range(len(s)):
            nPos=i
            i_number=s[i:].count(s[i])
            if i_number==1:
                continue
            while i_number!=1:
                if s[i+1]==s[i]:
                    i_number=i_number-1
                    nPos=i+1
                    if Maxsize!=max(Maxsize,len(s[i:nPos+1])):
                        Maxsize=len(s[i:nPos+1])
                        res_star,res_end=i,nPos+1
                        res+=s[i:nPos+1]
                else:
                    nPos=s[nPos+1:].index(s[i])
                    i_number=i_number-1
                    if s[i:nPos+1]==s[i:nPos+1:-1]:
                        if Maxsize!=max(Maxsize,len(s[i:nPos+1])):
                            res_star,res_end=i,nPos+1
                            res+=s[i:nPos+1]
        return res[res_star:res_end+1]

学习

  • str.find /str.index

  • 切片 回文子串

第二种方法:动态规划

执行用时:3828 ms; 内存消耗:11.7MB 效果:有点差 O(n2)

class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        max = 0
        palindromic = '' if len(s) == 0 else s[0]
        for i in range(len(s)):
            length = 1
            while i - length >=0 and i + length < len(s) and s[i-length] == s[i+length]:
                tmp = s[i-length:i+length+1]        ##奇回文
                if len(tmp) > max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
            length = 1
            while i - length + 1  >=0 and i + length < len(s) and s[i-length+1] == s[i+length]:
                tmp = s[i-length+1:i+length+1]        ##偶回文
                if len(tmp) > max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
        return palindromic

学习

  • 动态规划的思想是首先判断相邻的字符串是否是回文,然后继续判断连续的三个字符是否是回文,然后是四个,…,直到判断完整个字符串 ——对子串的判断;状态转换

  • 动态规划思想

  • 奇偶回文

第三种方法:Manacher马拉车法:添加辅助标志

执行用时:80 ms; 内存消耗:12MB 效果:非常好 O(n)

[理论文章!]

class Solution:
    #Manacher algorithm
    #http://en.wikipedia.org/wiki/Longest_palindromic_substring
    
    def longestPalindrome(self, s):
        # Transform S into T.
        # For example, S = "abba", T = "^#a#b#b#a#$".
        # ^ and $ signs are sentinels appended to each end to avoid bounds checking
        T = '#'.join('^{}$'.format(s))
        n = len(T)
        P = [0] * n
        C = R = 0
        for i in range (1, n-1):    ##首尾是终止符
            P[i] = (R > i) and min(R - i, P[2*C - i]) # equals to i' = C - (i-C)   #如果R>i且R-i大,则P[i]=P[j]
            # Attempt to expand palindrome centered at i                             两者相等,因为R范围内对称
            while T[i + 1 + P[i]] == T[i - 1 - P[i]]:
                P[i] += 1
    
            # If palindrome centered at i expand past R,
            # adjust center based on expanded palindrome.
            if i + P[i] > R:    #最终求出P[i]后设立新C,R C=center R=range 利用2*C-i拉着i走一样
                C, R = i, i + P[i]
    
        # Find the maximum element in P.
        maxLen, centerIndex = max((n, i) for i, n in enumerate(P))
        return s[(centerIndex  - maxLen)//2: (centerIndex  + maxLen)//2]    #因为len(P)=len(s)两倍多 

学习:

  • 两头需要边界符

  • 字符.join

  • 马拉车算法思想

  • list.enumerate()

第四种方法:新增字母判断是否仍是回文

执行用时:72 ms; 内存消耗:11.8MB 效果:非常好 O(n) 由于使用切片,所以比马拉车快

class Solution:
    # @return a string
    def longestPalindrome(self, s):
        if len(s)==0:
            return s
        maxLen=1
        start=0
        for i in xrange(len(s)):
            if i-maxLen >=1 and s[i-maxLen-1:i+1]==s[i-maxLen-1:i+1][::-1]:
                start=i-maxLen-1
                maxLen+=2
                continue


            if i-maxLen >=0 and s[i-maxLen:i+1]==s[i-maxLen:i+1][::-1]:
                start=i-maxLen
                maxLen+=1
        return s[start:start+maxLen]

学习

  • 思路是:遍历,第i个字母加上后面一个字母,看i-Max-1到i+1(就是从第i个往后面看)是不是回文,如果是,则回文串起点跟Max被记录下来;如果这样不是,那就看i-Max到i+1是不是回文,如果是也是一样的操作。

  • 因为前面字符串加上新的字符是回文的话,就只有两种可能性,
    ①:bb+b这种 ②:a bb+a这种,①记录下Max=2,
    ②则在a前面Max+1找,即是i-Max-1,然后记录下Max=3,
    做切片的时候就是start:start+Max,Max自动会-1的,保证半径符合。

posted @ 2019-10-09 23:35  黄龙士  阅读(262)  评论(0编辑  收藏  举报