找到最长的回文子串的算法的时间复杂度是多少?

时间:2018-03-30 21:04:28

标签: python string python-3.x substring palindrome

这是Python代码:

def is_palindrome(s):
    return s == s[::-1]


def longestp(s):
    if is_palindrome(s):
        return s

    maxp = s[0]

    for i in range(len(s)-1):
        half_length = len(maxp) // 2
        start = i - half_length
        end = i + half_length

        while start >= 0 and end <= len(s)-1:
            if is_palindrome(s[start:end+2]):
                if len(s[start:end+2]) > len(maxp):
                    maxp = s[start:end+2]
                end += 1
            elif is_palindrome(s[start:end+1]):
                if len(s[start:end+1]) > len(maxp):
                    maxp = s[start:end+1]
                start -= 1
                end += 1
            else:
                break

    return maxp

我最初认为它是O(n^3),因为有两个嵌套循环和字符串切片,但在我的测试中它几乎是线性的。是否有任何类型的输入,这个算法会变慢?

2 个答案:

答案 0 :(得分:2)

该算法看起来好像需要与

成比例的总时间
integral_0^N x dx = [(x^2)/2]_0^N = (N^2)/2 = O(N^2)

匹配ab*的字符串应该是最坏的情况。

这是一段代码,它通过实验证明了最坏的情况。

结构如下:

  1. 定义构造长度为worstCase
  2. 的“错误”字符串的N函数
  3. 测量函数在这些字符串上的时间
  4. 创建log(N)log(time(N))
  5. 的数据集
  6. 调整一条线,尝试估算线的斜率:这是p中的指数O(N^p)
  7. 以下是代码:

    def worstCase(length):
      return "a" + "b" * (length - 1)
    
    from time import clock
    from math import log
    
    xs = []
    ys = []
    for n in [4 * int(1000 * 1.2 ** n) for n in range(1, 20)]:
      s = worstCase(n)
      assert len(s) == n
      startTime = clock()
      p = longestp(s)
      endTime = clock()
      assert p == s[1:]
      t = endTime - startTime
      xs.append(log(n))
      ys.append(log(t))
      print("%d -> %f" % (n, endTime - startTime))
    
    from numpy import polyfit
    
    exponent, constant = polyfit(xs, ys, 1)
    
    print("Exponent was: %f" % (exponent))
    

    这是输出(需要一两分钟):

    4800 -> 0.057818
    5760 -> 0.078123
    6908 -> 0.105169
    8292 -> 0.145572
    9952 -> 0.197657
    11940 -> 0.276103
    14332 -> 0.382668
    17196 -> 0.534682
    20636 -> 0.747468
    24764 -> 1.048267
    29720 -> 1.475469
    35664 -> 2.081608
    42796 -> 2.939904
    51356 -> 4.216063
    61628 -> 5.963550
    73952 -> 8.691849
    88744 -> 12.126039
    106492 -> 19.684188
    127788 -> 24.942766
    Exponent was: 1.867208    
    

    它估计指数为~1.86,接近2而不是3。

答案 1 :(得分:1)

它绝对不是线性的。尝试使用包含大量回文的输入,但不是回文:

>>> timeit.timeit('longestp(x)', 'x="a"*100000+"b"', globals=globals(), number=1)
5.5123205203562975
>>> timeit.timeit('longestp(x)', 'x="a"*10000+"b"', globals=globals(), number=1)
0.08460151217877865

切片和s == s[::-1]具有比解释Python代码更好的常数因子,并且您需要确保内部循环不早break。这些影响可能会推迟你通过计时判断时间复杂度的尝试。

我也不认为它是O(n ^ 3)。由于break条件,嵌套循环不会以您可能直观的方式进行交互。内循环在整个算法过程中执行O(n)次迭代,因为在有限的迭代次数之后,len(maxp)增长,或循环break s。这个算法看起来最糟糕的是O(n ^ 2)。