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 的增强版本来解决更大的问题(ps 分别超过 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 字符串中的子字符串的主要内容,如果未能解决你的问题,请参考以下文章

[在python中使用正则表达式搜索字符串子字符串

PB中取字符串子串的函数是啥

如何更改python字符串子字符串信息

字符串子串去重之后的个数

sql server 里类似replace的字符串子串删除

使用 Python 的字符串子序列内核和 SVM