最长回文子串(python)

又开始刷题了,去年被这道题虐过,今天终于写了一个还能看的版本。

这个版本肯定不是最优,这篇博客主要记录一下解题的过程,反思如何构思代码。

最长回文子串

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/longest-palindromic-substring

难度:中等(我觉得这题完全配得上困难)

题目:

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

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:

输入: "cbbd"
输出: "bb"

解题过程

看到这题一开始是完全懵逼的,看着两个例子想了一个错的解法:用两个指针指向字符串的首尾,当两个指针所指的内容相同时,记录下两个索引值,不同时索引值归零,直到两个指针相遇。

这个解法很明显是错的,因为它假定了最长回文子串的中心一定是字符串中心

这个时候我看到了题目的三条提示:

How can we reuse a previously computed palindrome to compute a larger palindrome?

If “aba” is a palindrome, is “xabax” and palindrome? Similarly is “xabay” a palindrome?

Complexity based hint:
If we use brute-force and check whether for every start and end position a substring is a palindrome we have O(n^2) start - end pairs and O(n) palindromic checks. Can we reduce the time for palindromic checks to O(1) by reusing some previous computation.

前两条提示非常有用,我想到回文的特征是:呈中心对称。即,两个指针以同样的步幅从回文的中心出发,所指内容应该会一直保持一致。应该还是使用两个指针,但是是从内到外走,而不是从外到内

总体算法思路为:遍历整个字符串,将每个位置当作回文中心去找以这个字符为中心能形成的最长回文,然后选出整个字符串最长的回文

这里有一个很干扰我思路的问题出现了:就是'cbbd'这种情况。我的算法是无法输出'bb'的,在这个地方卡了很久。后来我决定把这种情况特殊处理,如果第i个字符串与第i+1个字符串相同,则将尾指针往后移。

我觉得有一个经验就是,在时间紧迫的时候,先把自己能想出来的完整算法写出来,再想办法去处理特殊情况,否则在那空想是很浪费时间的。

第一版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        longest_palindrome = {'length': 0, 'str': ''}
        for i in range(n):
            index1 = index2 = i
            if i+1 < n and s[i+1] == s[i]:
                index1 = i
                index2 = i+1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > longest_palindrome['length']:
                longest_palindrome['length'] = length
                longest_palindrome['str'] = s[index1:index2+1]
        return longest_palindrome['str']

结果:不通过

不通过的用例:

输入:
"ccc"
输出:
"cc"
预期:
"ccc"

在第二个c的位置,由于第三个c与第二个c相等,则index1=1,index2=2,将回文的中心变成了这两个字符,所以出错。

解决的思路是:找全所有所有相同的字符组成一个回文中心,即在处理第一个c的时候,就一直往后找,直到回文中心变为"ccc"

第二版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        longest_palindrome = {'length': 0, 'str': ''}
        for i in range(n):
            index1 = index2 = i
            while index2+1 < n and s[index1] == s[index2+1]:
                index2 += 1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > longest_palindrome['length']:
                longest_palindrome['length'] = length
                longest_palindrome['str'] = s[index1:index2+1]
        return longest_palindrome['str']

通过是通过了。就是执行时间980ms,位于前32%不太满意。可以再优化一下,不要每次找到更长的回文就切出来,只需要记下索引就好了

第三版:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        l_len = 0
        l_index1 = 0
        l_index2 = 0
        for i in range(n):
            index1 = index2 = i
            while index2+1 < n and s[index1] == s[index2+1]:
                index2 += 1
            while index1-1 >= 0 and index2+1 < n and s[index1-1] == s[index2+1]:
                index1 += -1
                index2 += 1
            length = index2 - index1 + 1
            if length > l_len:
                l_len = length
                l_index1 = index1
                l_index2 = index2
        return s[l_index1: l_index2+1]

执行时间优化至852ms,位于前26%。就这样吧,三个小时已经过去了,写算法题并不是写业务逻辑,性能比易读性更重要

猜你喜欢

转载自www.cnblogs.com/luozx207/p/12177741.html