python 的运行时 if 字符串中的子字符串
Posted
技术标签:
【中文标题】python 的运行时 if 字符串中的子字符串【英文标题】:Runtime of python's if substring in string 【发布时间】:2016-05-15 05:02:20 【问题描述】:下面if statement
的大O是什么?
if "pl" in "apple":
...
python如何判断字符串“pl”是否在字符串“apple”中找到的总体大O是多少
或字符串搜索中的任何其他子字符串。
这是测试子字符串是否在字符串中的最有效方法吗?是否使用与.find()
相同的算法?
【问题讨论】:
“什么是运行时”是什么意思?如果你问什么更快,你可以timeit
。
@MarounMaroun 我更新了问题位。抱歉不清楚
相关:Python - Cost of find() function
【参考方案1】:
你可以使用timeit
自己测试一下:
maroun@DQHCPY1:~$ python -m timeit 's = "apple";s.find("pl")'
10000000 loops, best of 3: 0.125 usec per loop
maroun@DQHCPY1:~$ python -m timeit 's = "apple";"pl" in s'
10000000 loops, best of 3: 0.0371 usec per loop
使用in
确实更快(0.0371 微秒与 0.125 微秒相比)。
具体实现可以看code itself。
【讨论】:
关于它的时间复杂度,我找到了一个很好的解释here。【参考方案2】:在 python 3.4.2 中,它们看起来像是在诉诸相同的功能,但时间上可能存在差异。例如s.find
首先需要查找字符串的find
方法等。
使用的算法是 Boyer-More 和 Horspool 的混合算法。
【讨论】:
【参考方案3】:我认为找出答案的最佳方法是查看来源。 This 看起来会实现__contains__
:
static int
bytes_contains(PyObject *self, PyObject *arg)
Py_ssize_t ival = PyNumber_AsSsize_t(arg, PyExc_ValueError);
if (ival == -1 && PyErr_Occurred())
Py_buffer varg;
Py_ssize_t pos;
PyErr_Clear();
if (PyObject_GetBuffer(arg, &varg, PyBUF_SIMPLE) != 0)
return -1;
pos = stringlib_find(PyBytes_AS_STRING(self), Py_SIZE(self),
varg.buf, varg.len, 0);
PyBuffer_Release(&varg);
return pos >= 0;
if (ival < 0 || ival >= 256)
PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
return -1;
return memchr(PyBytes_AS_STRING(self), (int) ival, Py_SIZE(self)) != NULL;
就stringlib_find()
而言,它使用fastsearch()
。
【讨论】:
【参考方案4】:时间复杂度平均为 O(N),最坏情况为 O(NM)(N 是较长字符串的长度,M 是您搜索的较短字符串)。从 Python 3.10 开始,启发式算法用于通过切换算法将最坏情况降低到 O(N + M)。
str.index()
、str.find()
、str.__contains__()
(in
运算符)和str.replace()
使用相同的算法;它是Boyer-Moore 的简化版,其思想来自Boyer–Moore–Horspool 和Sunday 算法。
见original stringlib discussion post,以及fastsearch.h
source code;在 Python 3.10 之前,基本算法自 introduction in Python 2.5 以来没有改变(除了 some low-level optimisations and corner-case fixes)。
该帖子包含算法的 Python 代码大纲:
def find(s, p): # find first occurrence of p in s n = len(s) m = len(p) skip = delta1(p)[p[m-1]] i = 0 while i <= n-m: if s[i+m-1] == p[m-1]: # (boyer-moore) # potential match if s[i:i+m-1] == p[:m-1]: return i if s[i+m] not in p: i = i + m + 1 # (sunday) else: i = i + skip # (horspool) else: # skip if s[i+m] not in p: i = i + m + 1 # (sunday) else: i = i + 1 return -1 # not found
以及速度比较。
在 Python 3.10 中,算法已更新为使用 Crochemore and Perrin's Two-Way string searching algorithm 的增强版本来解决更大的问题(p
和 s
分别超过 100 和 2100 个字符,s
至少 6 次只要p
),回复一个pathological edgecase someone reported。添加此更改的提交included a write-up on how the algorithm works。
双向算法的最坏情况时间复杂度为 O(N + M),其中 O(M) 是从s
搜索针构建移位表的预先支付的成本。一旦你有了那个表,这个算法确实有 O(N/M) 的最佳性能。
【讨论】:
什么是 delta1? @lahmania:在 original discussion post 中定义为 模式中最后一个字符的 Boyer-Moore delta1(或坏字符跳过)值。跨度>以上是关于python 的运行时 if 字符串中的子字符串的主要内容,如果未能解决你的问题,请参考以下文章