30

これは、「文字列に部分文字列が含まれる」問題を (より多くの) 任意の型に一般化したものです。

シーケンス (リストやタプルなど) が与えられた場合、別のシーケンスがその中にあるかどうかを判断する最良の方法は何ですか? おまけとして、サブシーケンスが開始する要素のインデックスを返す必要があります。

使用例 (Sequence in Sequence):

>>> 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

これまでのところ、私は総当たりに頼っているだけで、遅く、醜く、不器用に思えます。

4

10 に答える 10

24

2番目にKnuth-Morris-Prattアルゴリズムを使用します。ちなみに、あなたの問題(およびKMPソリューション)は、Python Cookbook2ndeditionのレシピ5.13です。関連するコードは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)
于 2009-01-08T20:42:46.140 に答える
12

これはブルートフォースアプローチです( @mcellaの回答O(n*m)に似ています)。小さな入力シーケンスの場合、純粋な Python での Knuth-Morris-Pratt アルゴリズムの実装( @Gregg Lind の回答を参照)よりも高速である可能性があります。O(n+m)

#!/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()

この場合、はどれくらい大きいのだろうか?

于 2009-01-08T21:55:59.470 に答える
9

簡単なアプローチ: 文字列に変換し、文字列の一致に依存します。

文字列のリストを使用した例:

 >>> 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
于 2015-04-03T00:07:40.433 に答える
6

文字列マッチングと同じこと... Knuth-Morris-Pratt 文字列マッチング

于 2009-01-08T20:05:09.397 に答える
4
>>> 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

申し訳ありませんが、私はアルゴリズムの専門家ではありません。現時点で頭の中で考えられる最速の方法です。少なくとも (私にとっては) 見栄えがよく、楽しくコーディングできました。;-)

ほとんどの場合、ブルート フォース アプローチが行っていることと同じです。

于 2009-01-08T20:26:54.223 に答える
2

別の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
于 2009-01-08T20:44:55.997 に答える
2

小さなパターンの場合は力ずくで問題ない場合があります。

より大きなものについては、Aho-Corasick アルゴリズムを見てください。

于 2009-01-08T19:51:19.207 に答える
1

パーティーには少し遅れましたが、文字列を使用した簡単なものを次に示します。

>>> 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


Ilya V. Schurovが 指摘したように、この場合のfindメソッドは、複数文字の文字列または複数桁の数字を含む正しいインデックスを返しません。

于 2016-10-27T20:21:49.403 に答える
-1

セットを使用した別のアプローチ:

set([5,6])== set([5,6])&set([4,'a',3,5,6])
True
于 2015-12-03T06:36:22.003 に答える