どの文字列検索アルゴリズムが実際に最速ですか?


27

私は、これが最速の文字列検索アルゴリズムであり、多くの意見を聞いてきましたが、最終的にはわかりません。

最速のアルゴリズムはボイヤー・ムーアであると言う人もいれば、Knuth-Morris-Prattが実際に速いと言う人もいます。

私はそれらの両方の複雑さを調べましたが、それらはほとんど同じに見えますO(n+m)。最悪の場合、ボイヤー・ムーアO(nm)はO(m + 2 * n)を持つクヌース・モリス・プラットに比べて複雑であることがわかりました。ここで、nはテキストの長さ、mはパターンの長さです。

私が知る限り、ボイル・ムーアはガリル規則を使用する場合、線形最悪のケースタイムを持ちます。

私の質問、実際には最速の文字列検索アルゴリズムです(この質問には、ボイヤー・ムーアとクヌース・モリス・プラットだけでなく、考えられるすべてのスティングアルゴリズムが含まれています)。

編集:この答えのため

私がまさに探しているのは:

テキストを考えるTと、パターンPIは、すべての外見見つけなければならないPでをT

また、PとTの長さはfromで[1,2 000 000]あり、プログラムは0.15秒未満で実行する必要があります。

KMPとRabin-Karpは問題について100%のスコアを得るのに十分であることは知っていますが、ボイヤー・ムーアを試して実装したいと思っていました。このタイプのパターン検索に最適なのはどれですか?


6
選択した言語でこれらをテストしたとき、何を見つけましたか?
ウォルター

4
いくつかのテストでは、ボイヤー・ムーアは他のKMPよりも優れていましたが、それらの「最良の」実装があるかどうかはわかりません。選択言語については、タグ内にあります:C ++(「選択言語」を書いてからそれを見たかどうかはわかりません)。PS最高のテストでテストしたかどうかもわかりません。
バンダモンタイギ13年


O(m + 2 * n)を持つKnuth-Morris-Pratt ... O(m + n)を意味します。
ジュール

適切なアルゴリズムの複雑さを備えたものを選択し、プロファイラーを手に持って微調整します-私のために常に働いていました。:-D

回答:


38

実行する検索の種類によって異なります。各アルゴリズムは特定のタイプの検索に対して特に優れたパフォーマンスを発揮しますが、検索のコンテキストについては述べていません。

検索タイプに関する典型的な考え方を次に示します。

  • Boyer-Moore:パターンを事前分析し、右から左に比較することで機能します。不一致が発生した場合、初期分析を使用して、検索対象のテキストに対してパターンをどれだけシフトできるかを判断します。これは、長い検索パターンに対して特にうまく機能します。特に、テキストのすべての文字を読む必要がないため、サブリニアにすることができます。

  • Knuth-Morris-Pratt:パターンの事前分析も行いますが、パターンの最初の部分ですでに一致したものを再利用して、再一致の必要がないようにします。検索パターンに再利用可能なサブパターンが含まれる可能性が高くなるため、アルファベットが小さい場合(f.ex. DNA塩基など)、これは非常にうまく機能します。

  • Aho-Corasick:多くの前処理が必要ですが、多くのパターンに対して必要です。同じ検索パターンを繰り返し検索することがわかっている場合は、検索ごとに1回ではなく1回だけパターンを分析する必要があるため、これは他の検索パターンよりもはるかに優れています。

したがって、CSで通常のように、全体的なベストに対する明確な答えはありません。むしろ、当面の仕事に適したツールを選択することです。

最悪の場合の推論に関する別の注意事項:最悪の場合を作成するために必要な検索の種類を検討し、これらが実際に関連するかどうかを十分に検討してください。たとえばO(mn)、ボイヤー・ムーアアルゴリズムの最悪の場合の複雑さは、検索パターンと、それぞれが1文字のみを使用するテキスト(での検索など)に由来します。そのような検索にaaaaaaaaaaaaaaaaaaaaaaaa本当に高速である必要がありますか?


私は使用する英語のアルファベットを全部持っているので、質問を更新しました。
バンダモンタイギ13年

はい、そのような検索でも高速である必要があります
バンダモンタイギ13年

1

私はこの質問に答えるのに少し遅れていますZ-Algorithmが、対応するものよりもはるかに速いと思います。最悪の場合の複雑さはO(m + n)であり、パターン/テキストの前処理を必要としません。他のアルゴリズムと比較して、コーディングも非常に簡単です。

次のように機能します。

たとえば、文字列がありますS ='abaaba'。のz(i)値を見つけますi=0 to len(S)-1。説明に入る前に、最初にいくつかの定義を置いておきます。

z(i)=いいえ。のプレフィックスの文字のうち、のプレフィックスSと一致するものs(i)

s(i)=のith接尾辞S

以下はのs(i)値ですs = 'abaaba'

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

今、すべてを見つけるにはO(N)が必要です z、前処理のオーバーヘッドなしで値です。このロジックを使用して特定の文字列のパターンをどのように一致させることができるのでしょうか?

例を見てみましょう。パターン(P):abaテキスト(T): aacbabcabaad

これをP $ Tの形式で入力します。($-パターンまたはテキストのどちらにも表示されない文字。$しばらくして重要性が出てきます。)

P$T = aba$aacbabcabaad

知ってる len(P) = 3。

のすべてのz値P$T

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)。ただし、この場合の状態は少し複雑でわかりにくいものになります。私は個人的に特殊なキャラクターのテクニックを使うことを好みます。


1
ここで自分で説明してもらえますか?外部サイトへのリンクを作成することで詳細を説明できますが、回答の中核は別のサイトへのリンクをたどるのではなく、回答自体の中にあるべきです。

Zアルゴリズムは、基本的にkmpと同じです。私はそれがはるかに高速であることを疑います。
トーマスエール

2
@ThomasAhleに同意します。コンピューティングz 前処理です。しかし、それは良い説明です。O(n)この答えにより、KMP前処理からZ前処理に変換する方法を提示しました。ここで
leewz

-1

仮想アドレス指定の形式でソフトウェアに実装されたコンテンツアドレス可能メモリを使用します(文字を文字に向ける)。

これは、平均的な文字列照合アルゴリズムには不必要です。

CAMは、最大約128文字のパターン(ASCIIの場合、Unicodeのみ64の場合)まで、膨大な数のパターンを同時に照合できます。そして、照合する文字列の文字の長さごとに1回の呼び出しと、最大パターン長の長さごとにメモリから1回のランダム読み取りが行われます。したがって、最大90,000,000個のパターンを同時に含む100,000文字の文字列を分析している場合(これほど大きなパターンのカウントを保存するには約128 GiBかかります)、RAMから12,800,000のランダム読み取りが必要になるため、1msで発生します。

仮想アドレス指定の仕組みは次のとおりです。

最初の文字を表す256個の開始アドレスで開始すると、これらの文字は次の256個の文字を指します。パターンが存在しない場合は、保存しません。

したがって、文字を文字にリンクし続けると、128スライスの仮想アドレスが仮想アドレスを指すようになります。

これは機能しますが、900,000,000個のパターンを同時に一致させるには、最後に追加するコツが1つあります。これらのレターバッファーを再利用することから始めますが、後で分散するという事実を利用しています。256文字すべてを割り当てるのではなく、コンテンツを一覧表示すると、速度がほとんど低下せず、最終的にすべてのレターポインターバッファーで使用されるレターが1つだけになるため、容量が100倍になります(「エスケープ')。

最も近い文字列の一致を取得する場合は、これらの多くを並行して実行し、階層で収集するため、エラーを偏りなく広げます。1つだけで最近傍にしようとすると、ツリーの開始に偏ります。


4
@MagnusRobertCarlWootは、roucer81と同じgavatarを持っていることを考えると、それはハッシュコードの衝突の天文学的な一致であるか、同じ電子メールアドレスを持っています。両方のアカウントの背後にいる同じ個人の場合は、「お問い合わせ」フォームを使用してそれらを統合し、この回答に対する賛成票から得た評判に対して適切なクレジットを取得する必要があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.