python's if substring in string 的运行时
Runtime of python's if substring in string
下面if statement
的大O是什么?
if "pl" in "apple":
...
python如何判断是否在字符串"apple"
中找到字符串"pl"的整体大O是什么
或字符串搜索中的任何其他子字符串。
这是测试字符串中是否包含子字符串的最有效方法吗?它使用与 .find()
相同的算法吗?
您可以使用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 usec 与 0.125 usec 相比)。
具体实现可以看code itself.
在python 3.4.2 中,他们似乎使用了相同的功能,但时间可能有所不同。例如s.find
首先需要查找字符串的find
方法等。
使用的算法是 Boyer-More 和 Horspool 的混合。
我认为最好的查找方法是查看源代码。 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()
, which uses fastsearch()
。
时间复杂度平均为 O(N),最坏情况为 O(NM)(N 是较长字符串的长度,M,您搜索的较短字符串)。从 Python 3.10 开始,启发式算法用于通过切换算法将最坏情况降低到 O(N + M)。
相同的算法用于 str.index()
、str.find()
、str.__contains__()
(in
运算符)和 str.replace()
;它是 Boyer-Moore with ideas taken from the Boyer–Moore–Horspool and Sunday 算法的简化。
参见original stringlib discussion post, as well as the fastsearch.h
source code; until Python 3.10, the base algorithm has not changed since introduction in Python 2.5 (apart from some low-level optimisations and corner-case fixes)。
post 包含算法的 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 for larger problems (with p
and s
longer than 100 and 2100 characters, respectively, with s
at least 6 times as long as p
), in response to a pathological edgecase someone reported. The commit adding this change included a write-up on how the algorithm works 的增强版本。
双向算法的最坏情况时间复杂度为 O(N + M),其中 O(M) 是预先支付的成本,用于从 s
搜索针。一旦你有了 table,这个算法确实有 O(N/M).
的最佳性能
下面if statement
的大O是什么?
if "pl" in "apple":
...
python如何判断是否在字符串"apple"
中找到字符串"pl"的整体大O是什么或字符串搜索中的任何其他子字符串。
这是测试字符串中是否包含子字符串的最有效方法吗?它使用与 .find()
相同的算法吗?
您可以使用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 usec 与 0.125 usec 相比)。
具体实现可以看code itself.
在python 3.4.2 中,他们似乎使用了相同的功能,但时间可能有所不同。例如s.find
首先需要查找字符串的find
方法等。
使用的算法是 Boyer-More 和 Horspool 的混合。
我认为最好的查找方法是查看源代码。 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()
, which uses fastsearch()
。
时间复杂度平均为 O(N),最坏情况为 O(NM)(N 是较长字符串的长度,M,您搜索的较短字符串)。从 Python 3.10 开始,启发式算法用于通过切换算法将最坏情况降低到 O(N + M)。
相同的算法用于 str.index()
、str.find()
、str.__contains__()
(in
运算符)和 str.replace()
;它是 Boyer-Moore with ideas taken from the Boyer–Moore–Horspool and Sunday 算法的简化。
参见original stringlib discussion post, as well as the fastsearch.h
source code; until Python 3.10, the base algorithm has not changed since introduction in Python 2.5 (apart from some low-level optimisations and corner-case fixes)。
post 包含算法的 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 for larger problems (with p
and s
longer than 100 and 2100 characters, respectively, with s
at least 6 times as long as p
), in response to a pathological edgecase someone reported. The commit adding this change included a write-up on how the algorithm works 的增强版本。
双向算法的最坏情况时间复杂度为 O(N + M),其中 O(M) 是预先支付的成本,用于从 s
搜索针。一旦你有了 table,这个算法确实有 O(N/M).