【问题标题】:Homework: Implementing the Z algorithm in python, it's really slow, slower than naive string search作业:用python实现Z算法,真的很慢,比naive string search慢
【发布时间】:2015-12-09 12:57:48
【问题描述】:

我必须实现 Z 算法并使用它在目标文本中搜索特定模式。我已经实现了我认为正确的算法和使用它的搜索功能,但它真的很慢。对于字符串搜索的幼稚实现,我始终得到低于 1.5 秒的时间,而对于 z 字符串搜索,我始终得到超过 3 秒的时间(对于我最大的测试用例),所以我必须做错事。结果似乎是正确的,或者至少对于我们给出的少数测试用例来说是正确的。我的咆哮中提到的功能的代码如下:

import sys
import time

# z algorithm a.k.a. the fundemental preprocessing algorithm
def z(P, start=1, max_box_size=sys.maxsize):
    n = len(P)
    boxes = [0] * n
    l = -1
    r = -1

    for k in range(start, n):
        if k > r:
            i = 0
            while k + i < n and P[i] == P[k + i] and i < max_box_size:
                i += 1
            boxes[k] = i
            if i:
                l = k
                r = k + i - 1
        else:
            kp = k - l
            Z_kp = boxes[kp]
            if Z_kp < r - k + 1:
                boxes[k] = Z_kp
            else:
                i = r + 1
                while i < n and P[i] == P[i - k] and i - k < max_box_size:
                    i += 1
                boxes[k] = i - k    
                l = k
                r = i - 1
    return boxes

# a simple string search
def naive_string_search(P, T):
    m = len(T)
    n = len(P)
    indices = []
    for i in range(m - n + 1):
        if P == T[i: i + n]:
            indices.append(i)
    return indices

# string search using the z algorithm.
# The pattern you're searching for is simply prepended to the target text
# and than the z algorithm is run on that concatenation
def z_string_search(P, T):
    PT = P + T
    n = len(P)
    boxes = z(PT, start=n, max_box_size=n)
    return list(map(lambda x: x[0]-n, filter(lambda x: x[1] >= n, enumerate(boxes))))

【问题讨论】:

  • 用 pypy 运行最简单
  • 你最大的测试用例有多大?
  • 例如在这种情况下 P:aaa T:aaaaaa 你的算法会产生不同的结果。
  • 那你的问题是什么?

标签: python algorithm string-matching


【解决方案1】:

您对 z 函数 def z(..) 的实现在算法上是可以的,并且在渐近上是可以的。

在最坏的情况下它具有 O(m + n) 时间复杂度,而在最坏情况下实现朴素字符串搜索具有 O(m*n) 时间复杂度,所以我认为问题出在您的测试用例中。

例如,如果我们采用这个测试用例:

T = ['a'] * 1000000 
P = ['a'] * 1000

我们将得到 z 函数:

real    0m0.650s
user    0m0.606s
sys 0m0.036s

对于简单的字符串匹配:

real    0m8.235s
user    0m8.071s
sys 0m0.085s

PS:你应该明白,在很多测试用例中,天真的字符串匹配也在线性时间内起作用,例如:

T = ['a'] * 1000000 
P = ['a'] * 1000000

因此,天真的字符串匹配的最坏情况是函数应该应用模式并一次又一次地检查。但在这种情况下,由于输入的长度,它只会做一次检查(它不能从索引 1 应用模式,所以它不会继续)。

【讨论】:

    猜你喜欢
    • 2018-08-11
    • 2020-08-16
    • 2017-02-17
    • 2013-01-16
    • 2017-11-10
    • 2010-12-28
    • 2013-01-25
    • 1970-01-01
    • 2012-01-30
    相关资源
    最近更新 更多