【发布时间】:2021-12-19 20:18:29
【问题描述】:
我目前正在学习和练习字符串算法。具体来说,我正在尝试用一些修改替换基于KMP 的字符串中的模式,这具有 O(N) 复杂度(我的实现如下)。
def replace_string(s, p, c):
"""
Replace pattern p in string s with c
:param s: initial string
:param p: pattern to replace
:param c: replacing string
"""
pref = [0] * len(p)
s_p = p + '#' + s
p_prev = 0
shift = 0
for i in range(1, len(s_p)):
k = p_prev
while k > 0 and s_p[i] != s_p[k]:
k = pref[k - 1]
if s_p[i] == s_p[k]:
k += 1
if i < len(p):
pref[i] = k
p_prev = k
if k == len(p):
s = s[:i - 2 * len(p) + shift] + c + s[i - len(p) + shift:]
shift += len(c) - k
return s
然后,我用python内置的str.replace函数写了同样的程序:
def replace_string_python(s, p, c):
return s.replace(p, c)
比较各种字符串的性能,我只附上一个例子,长度为 1e5 的字符串:
import time
if __name__ == '__main__':
initial_string = "a" * 100000
pattern = "a"
replace = "ab"
start = time.time()
res = replace_string(initial_string, pattern, replace)
print(time.time() - start)
输出(我的实现):
total time: 1.1617710590362549
输出(python 内置):
total time: 0.0015637874603271484
如您所见,通过 python str.replace 实现比 KMP 领先光年。所以我的问题是为什么? python C代码使用什么算法?
【问题讨论】:
-
我希望它仍然是 O(n),但它会更快,因为它是在 C 中实现的,而不必通过 Python 解释器。尝试将每个字符串延长 10 倍,看看这两个时间是否都增加了 ~10 倍。
-
举个例子,你在你的实现中构造了一堆字符串。您的
s = s[...:] + c + s[:...]将数据分配并复制到多达四个新的字符串对象中。 Python 对象比相应的 C 数据结构占用更多空间。而且,如果您将复制字符串内容所需的微循环计算为非原子的,那么从技术上讲,它不再是 O(N)(尽管您很难看到差异,因为这些循环比那里发生的其他事情)。 -
这里是C code header 的链接,用于
.replace方法。 -
@S3DEV,不幸的是,我对 C 的了解不够,无法理解该标头中发生了什么。基于之前的 cmets,我假设 str.replace 函数具有相同的 O(N) 复杂度,但它的编写效率更高。
-
... 因为在您的代码中,每个
+、每个:、每个[]、==和=都是对专门的、非常快速的函数的调用用 C 语言编写。但是文书工作(对象创建、对象销毁、内存管理......)加起来,突然之间,一堆快速函数最终运行得很慢。一个建筑大师可以做捷径;但如果你有一个完整的组织,捷径最终会害死人(或者让你的代码窒息,可能是这样)。
标签: python python-3.x string algorithm replace