【问题标题】:Fast String within List Searching列表搜索中的快速字符串
【发布时间】:2012-01-22 21:38:47
【问题描述】:

使用 Python 3,我有一个包含超过 100,000 个字符串 (list1) 的列表,每个字符串最多 300 个字符。我还有一个超过 900 万个子字符串的列表(list2)——我想计算 list2 中的一个子字符串出现在多少个元素中。例如,

list1 = ['cat', 'caa', 'doa', 'oat']
list2 = ['at', 'ca', 'do']

我希望函数返回(映射到 list2):

[2, 2, 1]

通常,这非常简单,需要的很少。但是,由于列表的大小,我遇到了效率问题。我想找到返回该计数器列表的最快方法。

我已经尝试过各种列表推导、生成器、地图、循环,但我还没有找到一种快速的方法来完成这项简单的任务。理论上完成这个目标最快的方法是什么,最好是快速采取O(len(list2)) 步骤?

【问题讨论】:

    标签: python string performance list


    【解决方案1】:

    设置M = len(list1)N = len(list2)

    对于list2 中的N 个条目中的每一个,您将不得不与list1 中的条目进行M 次比较。这是O(M x N) 的最坏情况运行时间。如果你更进一步,让list2 中的每个条目长度为 1,list1 中的每个条目长度为 300,那么你的运行时间为 O(300M x N)

    如果性能确实是个问题,请尝试动态编程。这是一个开始:

    1) 按长度升序对list2 进行排序,如下所示:

    ['scorch', 'scorching', 'dump', 'dumpster', 'dumpsters']
    

    2) 将它们分类为子列表,使得每个前面的条目都是前面条目的子集,如下所示:

    [['scorch', 'scorching'] , ['dump', 'dumpster', 'dumpsters']]
    

    3) 现在,如果您与list1 进行比较并且'scorch' 不在其中,那么您也不必搜索'scorching'。同样,如果'dump' 不存在,'dumpster''dumpsters' 也不存在

    注意最坏情况下的运行时间仍然相同

    【讨论】:

    • 这是个好主意,但 list2 中的每个子字符串至少在 list1 的一个元素中。
    • 这将需要大量开销,但您可以尝试根据 list1list2 的字符索引它们,因此如果 list1 的条目是 'abcd' 您不会检查list2 条目'efg',只检查位于'a''b''c''d' 的路径/分支下的list2 条目
    • 虽然会采取相同数量的步骤,对吧?现在,对于 list2 中的每个子字符串,我按 sum(1 for string in list1 if substring in string) 计数。检查未包含的字符的过程不会与 if/in 语句花费相同的时间吗?
    • @user1104160 我可能弄错了,但我认为您无法绕过O(300MxN) 的最坏情况。如果这是经常被调用的东西,我建议花时间在一个巨大的树/数组中根据长度和/或字母进行索引
    • 等一下,我正在尝试为您创建一个小例子……伙计,周五的方式太糟糕了
    【解决方案2】:

    不确定如何避免使用某种 O(n**2) 算法。这是一个简单的实现。

    >>> def some_sort_of_count(list1, list2):
    >>>     return [sum(x in y for y in list1) for x in list2]
    >>> 
    >>> list1 = ['cat', 'caa', 'doa', 'oat']
    >>> list2 = ['at', 'ca', 'do']
    >>> some_sort_of_count(list1, list2)
    [2, 2, 1]
    

    【讨论】:

      【解决方案3】:

      我相信使用Aho Corasick string matching 机器可以在线性时间内解决此任务。 请参阅this 回答以获取更多信息(也许您也可以从该问题的其他答案中获得想法 - 这几乎是相同的任务,我认为 Aho Corasick 是理论上最快的解决方法) .

      您将不得不以这种方式修改字符串匹配机器,而不是返回匹配项,而是将每个匹配的子字符串的计数器加一。 (这应该只是一个小的修改)。

      【讨论】:

        猜你喜欢
        • 2013-01-20
        • 2013-01-06
        • 2011-03-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-18
        • 1970-01-01
        • 2021-08-17
        • 2018-02-03
        相关资源
        最近更新 更多