【问题标题】:python string match time complexitypython字符串匹配时间复杂度
【发布时间】:2013-03-27 18:24:48
【问题描述】:

我需要这个问题的帮助。我认为时间复杂度是 O(n),但我朋友坚持认为这是 O(n^2)。原因之一是因为fn = fn[index+1 ::]

#filename: string_expression_matcher.cc
#string: stxpm.c
#should return



import sys
string = "string_expression_matcher.cc"
subStr = "stxpm.c"

fn = list(string)
for i in subStr:
    try:
        index = fn.index(i)
        fn = fn[index+1 ::]
    except:
        print ("can't dup")
        sys.exit()

print ("found") 

这是我的算法:

  1. 在 subStr 中:
    • 循环开始于:“string_expression_matcher.cc”
    • 此步骤剩余的字符串输出为:“tring_expression_matcher.cc”
  2. t in subStr
    • 循环开始于:“tring_expression_matcher.cc”
    • 剩下的是:“ring_expression_matcher.cc”
  3. x 在 subStr
    • 循环开始于:“ring_expression_matcher.cc”
    • 剩下的是:“pression_matcher.cc”
  4. p 在 subStr 中
    • 循环开始于:“pression_matcher.cc”
    • 剩下的是:“ression_matcher.cc”

以此类推到最后一步。

给定:

n = len(subStr)
m = len(string)`

这个程序的时间复杂度是多少? 谢谢大家,但我真的很想知道是 O(n) 还是 O(n^2)。我知道代码并不完美,但请关注时间复杂度.. 非常感谢

有谁知道 python 字符串复制是如何工作的?当我们执行 fn = fn[index+1 ::] 时会发生什么?

我问了一位杰出的工程师。他说结果是 O(m*n)。你呢?

【问题讨论】:

  • “如果我们在 m 或 n 中添加项目,时间复杂度是线性的......”到底是什么意思?
  • 忘记了我的评论。我想知道这个程序的时间复杂度是多少。请帮忙...

标签: python string time match complexity-theory


【解决方案1】:

您的算法(就比较次数而言)是O(n),其中n 是字符串的长度。在最坏的情况下,字符串和模式都是相同的,然后对于subStr 中的每个字符,您将移动到string 的下一个字符。相当于字符串的简单比较。

但是,就其他操作而言,您的实现可能是O(n^2),正如您在问题中提到的那样,原因如下:

fn = fn[index+1 ::]

这实际上是在复制字符串(假设上面的切片是作为副本实现的)。如果您再次考虑前面的示例,对于字符串中的每个字符,您必须复制所有剩余的字符,即O(n^2)。这是因为您将首先复制n-1 字符,然后是n-2n-3 等等,在最后一次迭代中您将只复制一个字符。那么要复制的项目总数将是n-1+n-2+...+1,作为arithmetic progression,等于(n-1)*((n-1)+1)/2 = (n-1)*n/2 = O(n^2)。对于其他情况,这可以推广到O(m*n),其中m 是模式的长度。

您的朋友可能想告诉您的是:您的算法是线性的,但您的实现不是。不过也很容易解决。使用@thkang 提供的解决方案或更透明的方法来消除隐藏的复杂性,例如:

try:
  si = iter(string)
  for c in subStr:
    while c != si.next():
      pass
except StopIteration:
  print "no match"
else:
  print "match"

【讨论】:

  • 这实际上是在复制字符串。如果您再次考虑前面的示例,对于字符串中的每个字符,您必须复制所有剩余的字符,这显然是 O(n^2)。你能再解释一下吗?为什么字符串复制会导致 O(n^2)?别忘了,字符串的大小正在缩小。谢谢
  • 尺寸在缩小,但仍有(n-1)*n/2份要完成,即O(n^2)。我已经编辑了答案以澄清一点。
  • 我刚刚发布了另一个答案 O(mn)。有两种不同长度的字符串。一个是m,另一个是n。在最坏的情况下,对于 n 中的每个元素,由于切片操作,需要复制 m 个元素。因此答案是 O(mn)。你同意吗?
  • 这是正确的,但如果m == n 你有O(n^2)。您不会对较短的字符串(模式)做出任何假设,所以再说一次:最坏的情况是两个相等的字符串。 O(n*m) 不是真正的线性,除非 nm 受常数限制。
  • 两者都是正确的,因为n^2m*n 的上限。您通常说O(n^2) 只是表示复杂性是二次的。如果你说O(m*n),你会给出更多细节,例如有人可以说:很好,在我的例子中m 总是大小为3,所以这实际上是线性的。我会说 O(m*n)quadratic 取决于某人希望收到的详细信息级别。如果您想挖掘问题,请打开有关大 O 表示法的单独问题以吸引更多答案。我们不应该在 cmets 中真正将原始问题扩展到这个级别。
【解决方案2】:

很抱歉,但这既不是 O(n) 也不是 O(n+m)。它们都比 O(n^2) 好,这个算法是 O(n^2)。为什么?

  1. 您迭代具有 O(n) 复杂度的源字符串
  2. 在每次迭代中,您在字符串上调用“索引”,该字符串也具有 O(n) 复杂度

因此,这具有 O(n^2) 限制的最坏情况性能,实际上是朴素搜索算法的一种实现。如果您想查看 O(n)/O(mn),我建议您查看 Boyer-Moore algorithm

【讨论】:

    【解决方案3】:

    如果您不想复制整个列表,请使用 .index 并指定起始索引。

    last_index = 0
    for i in subStr:
        try:
            last_index = fn.index(i, last_index) + 1
        except ValueError:
            print ("can't dup")
            sys.exit()
    else:
        #string matches
    

    【讨论】:

    • 谢谢,我只想知道是O(n)还是O(N^2)
    猜你喜欢
    • 2014-04-14
    • 1970-01-01
    • 2014-06-19
    • 2017-12-25
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 2018-01-06
    • 1970-01-01
    相关资源
    最近更新 更多