繁体   English   中英

在Python中查找字符串中最长的回文

[英]Finding the longest palindrome within a string in Python

我正在尝试解决 LeetCode 上的最长回文子串问题。 问题陈述是:

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

例子:

Input: "babad"

Output: "bab"

注意:“aba”也是一个有效的答案。 例子:

Input: "cbbd"

Output: "bb"

我提出了以下解决方案(包括一些测试用例):

import pytest

class Solution:
    def longestPalindrome(self, s):
        candidate = ""
        longest = ""
        contains_palindrome = False
        for i, char in enumerate(s):
            if i == 0:
                candidate = char
            elif i == 1:
                if s[1] == s[0]:
                    candidate = self.get_palindrome(s, start=0, end=1)
            elif i >= 2:
                if char == s[i-1]:
                    candidate = self.get_palindrome(s, start=i-1, end=i)
                elif char == s[i-2]:
                    candidate = self.get_palindrome(s, start=i-2, end=i)
            if len(candidate) > len(longest):
                longest = candidate
        return longest

    @staticmethod
    def get_palindrome(s, start, end):
        palindrome = s[start:end+1]
        while end < len(s) - 1:
            if s[end+1] == s[start] and Solution.all_same(palindrome):
                end += 1
                palindrome += s[end]
            else:
                break
        while (start > 0) and (end < len(s) - 1):
            start -= 1
            end += 1
            if s[start] == s[end]:
                palindrome = s[start] + palindrome + s[end]
            else:
                break
        return palindrome

    @staticmethod
    def all_same(items):
        return all(item == items[0] for item in items)


def test_1():
    assert Solution().longestPalindrome("babad") == "bab"

def test_2():
    assert Solution().longestPalindrome("cbbd") == "bb"

def test_3():
    assert Solution().longestPalindrome("abba") == "abba"

def test_4():
    assert Solution().longestPalindrome("a") == "a"

def test_5():
    assert Solution().longestPalindrome("ccc") == "ccc"

def test_6():
    assert Solution().longestPalindrome("aaaa") == "aaaa"

def test_7():
    assert Solution().longestPalindrome("aaabaaaa") == "aaabaaa"


if __name__ == "__main__":
    pytest.main([__file__])

问题是我收到“超出时间限制”错误:

在此处输入图片说明

我的理解是该算法的时间复杂度为 O(n^2),因为对于每个字符,它都会检查最多 n 个字符长的回文。 在 LeetCode 的解决方案中,还有 O(n^2) 算法(在 Java 中)。

我猜是 Python 的时间限制有点太严格了,它比 Java 慢。 还是我遗漏了什么,我的解决方案的时间复杂度实际上是否大于 O(n^2)?

您失败的测试字符串似乎只包含 a。 这是最糟糕的情况,它实际上是 O(n³) 而不是 O(n²),因为在all_same还有另一个隐藏循环。 (起初,我还认为字符串上的切片运算符[:]会复制,但事实并非如此。)

您需要调用all_same ,因为您在主函数中区分了“aa”和“aba”。 但是您不需要在循环中执行此操作,因为您将在get_palindrome的第一个while循环中get_palindrome添加相同的字母。 因此,快速解决方法是测试所有字符是否仅相同一次:

    if Solution.all_same(palindrome):
        while end < len(s) - 1:
            if s[end+1] == s[start]:
                end += 1
                palindrome += s[end]
            else:
                break

现在all_same os 运行在两个或三个字母的字符串上并且速度会很快。

更好的解决方案根本不需要all_same 当您可以只传递“b”并让该函数完成其余工作时,为什么要将“aba”传递给get_palindrome

        elif i >= 2:
            if char == s[i-1]:
                candidate = self.get_palindrome(s, start=i-1, end=i)
            else:
                candidate = self.get_palindrome(s, start=i, end=i)

总的来说,所有的break和不必要的区分大小写的代码看起来相当不整洁。 为什么将索引和palindrome作为单独的实体保存在get_palindrome ,您必须保持同步?

这是我认为更整洁的版本:

class Solution:
    def longestPalindrome(self, s):
        longest = ""

        for i, _ in enumerate(s):
            candidate = self.get_palindrome(s, start = i, end = i)

            if len(candidate) > len(longest):
                longest = candidate

        return longest

    @staticmethod
    def get_palindrome(s, start, end):
        while end + 1 < len(s) and s[end+1] == s[start]:
            end += 1

        while start > 0 and end + 1 < len(s) and s[start - 1] == s[end + 1]:
            start -= 1
            end += 1

        return s[start:end + 1]

即便如此,仍有改进的空间:对于字符串“aaaa”,代码仍会考虑“aaaa”、“aaa”、“aa”和“a”。 get_palindrome的第一个while会一路走下去,但没有机会找到更好的命中。 我们可以通过在 main 函数中找到相同字母的片段来改进这一点:

class Solution:
    def longestPalindrome(self, s):
        longest = ""
        i = 0
        l = len(s)

        while i < l:
            end = i

            while end + 1 < l and s[end + 1] == s[i]:
                end += 1

            candidate = self.get_palindrome(s, i, end)

            if len(candidate) > len(longest):
                longest = candidate

            i = end + 1

        return longest

    @staticmethod
    def get_palindrome(s, start, end):
        while start > 0 and end + 1 < len(s) and s[start - 1] == s[end + 1]:
            start -= 1
            end += 1

        return s[start:end + 1]

这对于像“abababab”这样的字符串仍然不是理想的,但在你的情况下应该足够快。

我尝试了“动态编程”的想法,即找到“0_th 阶”回文的中心,然后随着深度j的增加和不匹配的发生进行修剪

修剪是在 list comps 内完成的,应该相对较快,但仍然是 O(n^2)

class Solution:
    def longestPalindrome(self, s):

        s = '>' + s + '<'  # add guard values

        # make lists of '0_th order' palindrome 'centers', even and odd

        evn = [i for i, a in enumerate(zip(s, s[1:])) if a[0] == a[1]]

        odd = [i + 1 for i, a in enumerate(zip(s, s[2:])) if a[0] == a[1]]

        # prune lists of centers when elements +/- j from centers don't match

        evn_last, odd_last = [[1], 0], [[1], 1]

        j = 1
        while evn:
            evn_last = (evn, j)
            evn = [e for e in evn if s[e - j] == s[e + j + 1]]
            j += 1

        j = 1
        while odd:
            odd_last = (odd, j)
            odd = [e for e in odd if s[e - j] == s[e + j]]
            j += 1

        # determine longest, construct palindrome

        if 2 * evn_last[1] > 2 * odd_last[1] - 1:

            cntr = evn_last[0][0]
            pal = s[cntr] + s[cntr + 1]
            for i in range(1, evn_last[1]):
                pal = s[cntr - i] + pal + s[cntr + i + 1]
        else:
            cntr = odd_last[0][0]
            pal = s[cntr]
            for i in range(1, odd_last[1]):
                pal = s[cntr - i] + pal + s[cntr + i]
        return pal

如果我错误地粘贴到 Class 包装器中,我深表歉意 - OOP 不是我的事
确实通过了你的测试

可能已经想通了调用实例,明显的重命名

S = Solution()

%timeit S.fred_longestPalindrome("aba"*300)
17.8 ms ± 230 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit S.Kurt_longestPalindrome("aba"*300)
52.8 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

这是我发现获得最长回文及其长度的方法。 我认为这很容易理解。

首先我将这个词添加到一个字符数组中,然后我检查了第一个字母和所有其他字母的向后。 然后像这样继续下一个字符。 使用 if else 和 for 循环来获得答案,最后使用哈希集我得到了最长的回文。

public static void main(String[] args) {

    Scanner in = new Scanner(System.in);

System.out.println(longestPalSubstr(in.nextLine().toLowerCase()));


}




static String longestPalSubstr(String str) {

       char [] input = str.toCharArray();
       Set<CharSequence> out = new HashSet<CharSequence>();

      int n1 = str.length()-1;


      for(int a=0;a<=n1;a++)
       {
          for(int m=n1;m>a;m--)
          {

          if(input[a]==input[m])
          {

           String nw = "",nw2="";

           for (int y=a;y<=m;y++)
           {

                nw=nw+input[y];
           }
           for (int t=m;t>=a;t--)
           {

               nw2=nw2+input[t];
           }


           if(nw2.equals(nw))
           {

                out.add(nw);


               break;
           }
       }

     }

   }


    int a = out.size();
    int maxpos=0;
    int max=0;
    Object [] s = out.toArray();

    for(int q=0;q<a;q++)
    {

        if(max<s[q].toString().length())
        {
            max=s[q].toString().length();
            maxpos=q;
        }
    }


   String output = "longest palindrome is : "+s[maxpos].toString()+" and the lengths is : "+ max; 
   return output;



}

此方法将返回最大长度回文及其长度。 这是我尝试并得到答案的一种方式。 无论是奇数长度还是偶数长度,此方法都将运行。

class Solution:
    def longestPalindrome(self, s):   
        paliandr = ''
        len_s = len(s)


        def if_pal_singl(s,i,dabl):
            pal = s[i-dabl:i+1]
            indx_left = i-dabl
            indx_right = i
            while (indx_left-1 in range(len_s) and indx_right+1 in range(len_s)):
                indx_left -=1
                indx_right +=1
                if s[indx_left] == s[indx_right]:
                    pal = s[indx_left]+pal+s[indx_right]
                else: 
                    break
            return pal  


        dabl = 0        
        for i in range(1,len_s-1):
            if s[i] == s[i+1]:
                dabl+=1
                continue
            pal = if_pal_singl(s,i,dabl)
            dabl = 0
            if len(pal) > len(paliandr):
                paliandr = pal
        print (paliandr)
if __name__ == "__main__":
    Solution().longestPalindrome('abababab')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM