私はしばらくの間、最速の文字列検索アルゴリズムで立ち往生してきましたが、多くの意見を聞きましたが、最終的にはわかりません。
最速のアルゴリズムはボイヤー・ムーアであると言う人もいれば、クヌース・モリス・プラットが実際には速いと言う人もいます。
私は両方の複雑さを調べましたが、それらはほとんど同じO(n+m)
に見えます。最悪のシナリオでは、Boyer-MooreはO(m + 2 * n)を持つKnuth-Morris-Prattと比較してO(nm)
の複雑さを持っていることがわかりました。ここで、n =テキストの長さ、m =パターンの長さ。
私が知る限り、ガリルルールを使用すると、ボイヤームーアは線形最悪のケースタイムになります。
私の質問、Over allこれは実際には最速の文字列検索アルゴリズムです(この質問には、Boyer-MooreとKnuth-Morris-Prattだけでなく、すべての可能なスティングアルゴリズムが含まれます)。
編集:this answer による
私が正確に探しているのは:
テキストT
とパターンP
を指定すると、P
内のT
のすべての外観を見つける必要があります。
また、PとTの長さは[1,2 000 000]
およびプログラムは0.15秒未満で実行する必要があります。
KMPとRabin-Karpは問題の100%のスコアを取得するのに十分であることを知っていますが、ボイヤー・ムーアを試して実装したいと思っていました。このタイプのパターン検索に最適なのはどれですか。
実行する検索の種類によって異なります。それぞれのアルゴリズムは、特定のタイプの検索で特に効果的ですが、検索のコンテキストについては説明していません。
検索タイプに関する一般的な考え方を以下に示します。
ボイヤー・ムーア:パターンを事前に分析し、右から左に比較することで機能します。不一致が発生した場合、最初の分析を使用して、パターンをどのくらいシフトできるかを決定します。検索されるテキスト。これは、長い検索パターンで特に効果的です。特に、テキストの1つ1つの文字を読み取る必要がないため、直線的でない場合があります。
Knuth-Morris-Pratt:パターンも事前に分析しますが、パターンの最初の部分で既に一致したものはすべて再利用して、再一致する必要がないようにします。検索パターンに再利用可能なサブパターンが含まれる可能性が高くなるので、アルファベットが小さい(例:DNA塩基)場合、これは非常にうまく機能します。
Aho-Corasick:多くの前処理が必要ですが、多くのパターンで必要です。同じ検索パターンを何度も検索することがわかっている場合、パターンを分析する必要があるのは1回の検索ではなく1回だけなので、これは他の検索パターンよりもはるかに優れています。
したがって、CSでいつものように、全体的に最良に対する明確な答えはありません。それはむしろ、目前の仕事に適したツールを選択することの問題です。
最悪の場合の推論に関する別のメモ:その最悪の場合を作成するために必要な種類の検索を検討し、これらがあなたのケースに本当に関連しているかどうかを十分に検討してください。たとえば、Boyer-MooreアルゴリズムのO(mn)
最悪の場合の複雑さは、それぞれが1文字だけを使用する検索パターンとテキストに由来します(aaa
でaaaaaaaaaaaaaaaaaaaaa
を見つけるなど)-本当に高速である必要がありますか?そのような検索のために?
この質問に答えるのは少し遅れますが、_Z-Algorithm
_は他の質問よりもはるかに高速だと思います。その最悪の場合の複雑さはO(m + n)であり、パターン/テキストの前処理は必要ありません。また、他のアルゴリズムと比較してコーディングが非常に簡単です。
次のように動作します。
たとえば、文字列_S ='abaaba'
_があります。 z(i)
のi=0 to len(S)-1
値を見つけます。説明に入る前に、いくつかの定義を最初に置きます。
z(i)
=いいえ。 s(i)
のプレフィックスと一致するS
のプレフィックスの文字数。
s(i)
= ith
S
のサフィックス。
以下は、_s = 'abaaba'
_のs(i)
の値です。
_s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'
_
Z値はそれぞれ
_z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1
_
アルゴリズムの詳細については、次のリンクを参照してください。
http://codeforces.com/blog/entry/3107
https://www.youtube.com/watch?v=MFK0WYeVEag
ここで、前処理のオーバーヘッドなしにすべてのz
値を見つけるにはO(N)が必要です。このロジックを使用して、与えられた文字列?
例で見てみましょう。パターン(P):aba
、テキスト(T):aacbabcabaad
。
これをP $ Tの形式で入力します。 (_$
_-パターンにもテキストにも表示されない任意の文字。しばらくして_$
_の重要性に気づくでしょう。)
_P$T
_ = _aba$aacbabcabaad
_
len(P)
= 3です。
_P$T
_のすべてのz値は
_z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0
_
ここでz(i)
= len(P)
になります。 _Ans = 11.
_したがって、パターンはAns-len(P)-1
= _7
_にあります。 _-1
_は_$
_文字用です。
では、なぜ_$
_またはそのような特殊文字が重要なのでしょうか。 _P = 'aaa'
_および_T = 'aaaaaaa'
_を検討してください。特殊文字がない場合、すべてのz(i)
には増分値があります。以下の式を使用して、テキスト内のパターンの位置を見つけることができます。
条件:z(i)
> = len(P)
および位置:Ans-len(P)
。しかし、この場合の状態は少しトリッキーで混乱を招きます。個人的には特殊キャラクターのテクニックを使う方が好きです。