代码之家  ›  专栏  ›  技术社区  ›  Gregg Lind

在Python中确定一个序列是否在另一个序列中的最佳方法

  •  23
  • Gregg Lind  · 技术社区  · 17 年前

    这是将“字符串包含子字符串”问题推广到(更多)任意类型。

    >>> seq_in_seq([5,6],  [4,'a',3,5,6])
    3
    >>> seq_in_seq([5,7],  [4,'a',3,5,6])
    -1 # or None, or whatever
    

    到目前为止,我只是依靠蛮力,它看起来缓慢、丑陋、笨拙。

    9 回复  |  直到 6 年前
        1
  •  24
  •   Federico A. Ramponi    17 年前

    我支持Knuth-Morris-Pratt算法。顺便说一下,你的问题(和KMP解决方案)正是配方5.13中的问题 Python Cookbook 第二版。您可以在以下位置找到相关代码: http://code.activestate.com/recipes/117214/

    它发现 全部的 给定序列中的正确子序列,并应用作迭代器:

    >>> for s in KnuthMorrisPratt([4,'a',3,5,6], [5,6]): print s
    3
    >>> for s in KnuthMorrisPratt([4,'a',3,5,6], [5,7]): print s
    (nothing)
    
        2
  •  11
  •   Community Mohan Dere    9 年前

    O(n*m) (类似于 @mcella's answer )。它可能比纯Python中的Knuth-Morris-Pratt算法实现更快 O(n+m) @Gregg Lind answer )为了 小的

    #!/usr/bin/env python
    def index(subseq, seq):
        """Return an index of `subseq`uence in the `seq`uence.
    
        Or `-1` if `subseq` is not a subsequence of the `seq`.
    
        The time complexity of the algorithm is O(n*m), where
    
            n, m = len(seq), len(subseq)
    
        >>> index([1,2], range(5))
        1
        >>> index(range(1, 6), range(5))
        -1
        >>> index(range(5), range(5))
        0
        >>> index([1,2], [0, 1, 0, 1, 2])
        3
        """
        i, n, m = -1, len(seq), len(subseq)
        try:
            while True:
                i = seq.index(subseq[0], i + 1, n - m + 1)
                if subseq == seq[i:i + m]:
                   return i
        except ValueError:
            return -1
    
    if __name__ == '__main__':
        import doctest; doctest.testmod()
    

    我想知道这个房间有多大 小的 在这种情况下?

        3
  •  8
  •   vexed    11 年前

    使用字符串列表的示例:

     >>> f = ["foo", "bar", "baz"]
     >>> g = ["foo", "bar"]
     >>> ff = str(f).strip("[]")
     >>> gg = str(g).strip("[]")
     >>> gg in ff
     True
    

    使用字符串元组的示例:

    >>> x = ("foo", "bar", "baz")
    >>> y = ("bar", "baz")
    >>> xx = str(x).strip("()")
    >>> yy = str(y).strip("()")
    >>> yy in xx
    True
    

    使用数字列表的示例:

    >>> f = [1 , 2, 3, 4, 5, 6, 7]
    >>> g = [4, 5, 6]
    >>> ff = str(f).strip("[]")
    >>> gg = str(g).strip("[]")
    >>> gg in ff
    True
    
        4
  •  6
  •   nlucaroni    17 年前
        5
  •  4
  •   mcella    17 年前
    >>> def seq_in_seq(subseq, seq):
    ...     while subseq[0] in seq:
    ...         index = seq.index(subseq[0])
    ...         if subseq == seq[index:index + len(subseq)]:
    ...             return index
    ...         else:
    ...             seq = seq[index + 1:]
    ...     else:
    ...         return -1
    ... 
    >>> seq_in_seq([5,6], [4,'a',3,5,6])
    3
    >>> seq_in_seq([5,7], [4,'a',3,5,6])
    -1
    

    对不起,我不是一个算法专家,这只是我目前能想到的最快的事情,至少我觉得它看起来不错(对我来说),而且我很喜欢编写代码

    很可能这和你的暴力手段是一样的。

        6
  •  2
  •   Doug Currie    17 年前

    对于小图案,蛮力可能很好。

    对于较大的,请查看 Aho-Corasick algorithm

        7
  •  2
  •   danodonovan    14 年前

    以下是另一个KMP实现:

    from itertools import tee
    
    def seq_in_seq(seq1,seq2):
        '''
        Return the index where seq1 appears in seq2, or -1 if 
        seq1 is not in seq2, using the Knuth-Morris-Pratt algorithm
    
        based heavily on code by Neale Pickett <neale@woozle.org>
        found at:  woozle.org/~neale/src/python/kmp.py
    
        >>> seq_in_seq(range(3),range(5))
        0
        >>> seq_in_seq(range(3)[-1:],range(5))
        2
        >>>seq_in_seq(range(6),range(5))
        -1
        '''
        def compute_prefix_function(p):
            m = len(p)
            pi = [0] * m
            k = 0
            for q in xrange(1, m):
                while k > 0 and p[k] != p[q]:
                    k = pi[k - 1]
                if p[k] == p[q]:
                    k = k + 1
                pi[q] = k
            return pi
    
        t,p = list(tee(seq2)[0]), list(tee(seq1)[0])
        m,n = len(p),len(t)
        pi = compute_prefix_function(p)
        q = 0
        for i in range(n):
            while q > 0 and p[q] != t[i]:
                q = pi[q - 1]
            if p[q] == t[i]:
                q = q + 1
            if q == m:
                return i - m + 1
        return -1
    
        8
  •  1
  •   bogfard    9 年前

    我参加聚会有点晚了,但这里有一些使用字符串的简单方法:

    >>> def seq_in_seq(sub, full):
    ...     f = ''.join([repr(d) for d in full]).replace("'", "")
    ...     s = ''.join([repr(d) for d in sub]).replace("'", "")
    ...     #return f.find(s) #<-- not reliable for finding indices in all cases
    ...     return s in f
    ...
    >>> seq_in_seq([5,6], [4,'a',3,5,6])
    True
    >>> seq_in_seq([5,7], [4,'a',3,5,6])
    False
    >>> seq_in_seq([4,'abc',33], [4,'abc',33,5,6])
    True
    


    正如 这个 在这种情况下,方法将不会返回包含多个字符串或多位数的正确索引。

        9
  •  0
  •   miguelghz    10 年前

    set([5,6])== set([5,6])&set([4,'a',3,5,6])
    True
    
        10
  •  0
  •   GrantJ    5 年前

    from collections import deque
    from itertools import islice
    
    def seq_in_seq(needle, haystack):
        """Generator of indices where needle is found in haystack."""
        needle = deque(needle)
        haystack = iter(haystack)  # Works with iterators/streams!
        length = len(needle)
        # Deque will automatically call deque.popleft() after deque.append()
        # with the `maxlen` set equal to the needle length.
        window = deque(islice(haystack, length), maxlen=length)
        if needle == window:
            yield 0  # Match at the start of the haystack.
        for index, value in enumerate(haystack, start=1):
            window.append(value)
            if needle == window:
                yield index
    

    deque实现的一个优点是,它只在干草堆上进行一次线性传递。因此,如果干草堆是流式的,那么它仍然可以工作(与依赖切片的解决方案不同)。

    str.index