大量のテキスト内のすべての一致を見つけるための効率的なアルゴリズムの提案を探しています。検索する用語はリストに含まれ、1000以上の可能性があります。検索語は1つ以上の単語である可能性があります。
明らかに、各検索用語と比較して、テキストを複数回通過することができました。あまり効率的ではありません。
検索語の順序付けと一般的なサブセグメントの組み合わせについて考えました。そうすれば、多数の用語をすばやく削除できます。言語はC++で、ブーストを使用できます。
検索用語の例としては、フォーチュン500企業名のリストがあります。
アイデア?
この問題は集中的に研究されてきました。不思議なことに、1つのパターン/文字列を検索するための最良のアルゴリズムは、複数文字列のマッチングに簡単に外挿することはできません。
ザ・ 「grep」 ファミリは、非常に効率的な方法でマルチ文字列検索を実装します。それらを外部プログラムとして使用できる場合は、それを実行してください。
本当にアルゴリズムを実装する必要がある場合、最も速い方法はagrepが行うことを再現することだと思います(agrepはマルチストリングマッチングに優れています!)。 ここ はソースファイルと実行可能ファイルです。
そして ここ 使用されたアルゴリズム、理論的背景、および文字列照合に関する多くの情報とポインタを説明する論文があります。
注意事項:複数文字列の照合は、クヌース、ボイヤー、ムーア、バエザイェーツなどの人々によって徹底的に研究されてきました。本当に高速なアルゴリズムが必要な場合は、広い肩に立つことを躊躇しないでください。車輪の再発明をしないでください。
単一パターンの場合と同様に、複数パターンマッチングにはいくつかのアルゴリズムがあり、目的に最も適したものを見つける必要があります。論文 マルチパターン検索の高速アルゴリズム(アーカイブされたコピー) Aho-Corasick(Knuth-Morris-Prattのマルチパターンバージョンの一種)を含む、それらのほとんどのレビューを行います線形の複雑さを持つアルゴリズム)とCommentz-Walter(Boyer-MooreとAho-Corasickの組み合わせ)、および複数のパターンを照合するタスクにBoyer-Mooreのアイデアを使用する新しいアルゴリズムを導入します。
その論文で言及されていない代替のハッシュベースのアルゴリズムは ラビン-カープアルゴリズム であり、これは他のアルゴリズムよりも最悪の場合の複雑さが大きいが、ハッシュを介して線形係数を減らすことによってそれを補償する。どちらが優れているかは、最終的にはユースケースによって異なります。最速のものを選択したい場合は、それらのいくつかを実装し、アプリケーションでそれらを比較する必要があるかもしれません。
テキストの大部分が静的な英語のテキストであり、単語全体を一致させる必要があると仮定すると、次のことを試すことができます(質問で「一致」とは何か、どのようなテキストを見ているかなどを明確にする必要があります)。
まず、ドキュメント全体を Trie または [〜#〜] dawg [〜#〜] に前処理します。
Trie/Dawgには次のプロパティがあります。
トライ/ドーグと長さKの検索語が与えられた場合、O(K)時間内に、単語に関連付けられたデータを検索できます(または一致するものがないかどうかを判断できます)。
DAWGを使用すると、トライと比較してより多くのスペースを節約できます。多くの単語に共通のプレフィックスがあり、DAWGは共通のプレフィックスと共通のサフィックスプロパティを利用するという事実を利用しようとします。
トライでは、Wordの位置のリストも正確に維持します。たとえば、テキストが
That is that and so it is.
that
の最後のtのノードにはリスト{1,3}があり、is
のsのノードにはリスト{2,7}が関連付けられています。
これで、単一の単語検索用語を取得すると、トライを歩き、その単語に一致するリストを簡単に取得できます。
複数の単語の検索語を取得した場合は、次のことができます。
検索語の最初の単語でトライを歩きます。一致するもののリストを取得し、hashTableH1に挿入します。
次に、検索語の2番目の単語でトライを歩きます。一致するもののリストを取得します。一致位置xごとに、x-1がHashTableH1に存在するかどうかを確認します。その場合は、xを新しいハッシュテーブルH2に追加します。
3番目の単語でトライを歩き、一致するもののリストを取得します。一致位置yごとに、y-1がH3に存在するかどうかを確認し、存在する場合は、新しいハッシュテーブルH3に追加します。
などを続けます。
最後に、検索フレーズに一致するリストが表示され、フレーズの最後の単語の位置が示されます。
リスト内の位置のソートされたリストを維持し、バイナリ検索を実行することにより、フレーズマッチングステップを最適化できる可能性があります。 H2の各キーkについて、検索語3のソート済みリストでk + 1をバイナリ検索し、見つかった場合はk +1をH3に追加します。
この問題の最適な解決策は、 サフィックスツリー (または サフィックス配列 )を使用することです。これは基本的に、文字列のすべてのサフィックスのトライです。長さがO(N)
のテキストの場合、これはO(N)
に組み込むことができます。
次に、長さk
の文字列のすべてのm
オカレンスは、O(m + k)
で最適に回答できます。
接尾辞木を使用して、たとえば最長の回文、最長の共通部分文字列、最長の繰り返し部分文字列など。
これは、数百万/数十億塩基の長さのDNA文字列を分析するときに使用する典型的なデータ構造です。
それで、あなたはたくさんの検索用語を持っていて、それらのどれかが文書にあるかどうかを見たいですか?
純粋にアルゴリズム的に、正規表現エンジンが_/ant|ape/
_を調べ、「類人猿」のaを適切に短絡する場合は、すべての可能性をアルファベット順に並べ替え、パイプで結合し、正規表現として使用できます。 「ant」では見つかりませんでした。そうでない場合は、正規表現の「プリコンパイル」を実行し、結果を最小のオーバーラップまで「スキッシュ」することができます。つまり上記の場合、/a(nt|pe)/
など、文字ごとに再帰的に。
ただし、上記を実行することは、すべての検索文字列を26-aryツリー(26文字、数字の場合はそれ以上)に配置するのとほとんど同じです。長さの文字ごとに1レベルの深さを使用して、文字列をツリーにプッシュします。
検索語の数が多い場合は、検索語を使用してこれを実行し、「この単語は検索語のリスト内のすべてに一致しますか」という超高速を作成できます。
ドキュメントが静的で検索用語が大幅に変更される場合は、理論的には逆のこともできます。つまり、ドキュメントをツリーにパックしてから、その上で検索用語を使用します。
必要な最適化の量によって異なります...
あなたが探している検索用語の単語ですか、それとも完全な文である可能性がありますか?
単語だけの場合は、すべての単語から 赤黒木 を作成し、ツリー内の各単語を検索することをお勧めします。
それが文である可能性がある場合、それははるかに複雑になる可能性があります...(?)