web-dev-qa-db-ja.com

文字列内の部分文字列を検索するための高速アルゴリズム

文字列内の部分文字列を検索するためにJavaで使用できる効率的なアルゴリズム(またはライブラリ)が欲しいのですが。

私がしたいのは:

入力文字列が与えられた場合-[〜#〜] instr [〜#〜]

「BCDEFGH」

そして、候補文字列のセット-[〜#〜] cand [〜#〜]

「AB」、「CDE」、「FG」、「H」、「IJ」

[〜#〜] cand [〜#〜]文字列を[〜#〜] instr [〜#〜]

この例では、「CDE」、「FG」、および「H」と一致します(「AB」と「IJ」は一致しません)。

候補となる文字列は数千にもなる可能性がありますが(CANDの場合)、さらに重要なことに、この検索を何百万回も実行するため、高速である必要があります。

Char配列を使用したいのですが。また、私は検索の分散など、アーキテクチャソリューションに精通していない-ローカルで実行するための最も効率的な関数/アルゴリズム。

さらに、CANDおよびINSTR内のすべての文字列はすべて比較的小さく(50文字未満)です。つまり、ターゲット文字列INSTRは候補文字列に比べて長くありません。


UpdateCAND文字列のセットは、INSTRのすべての値にわたって不変です。

更新一致があったことだけを知る必要があります-一致が何であるかを知る必要はありません。

最終更新実装が簡単なため、AhoCorsickとRabin-Karpを試すことにしました。可変長パターンがあるので、各パターンの最初のn文字をハッシュする変更されたRabin-Karpを使用しました。ここで、nは最小パターンの長さであり、Nはローリングサブストリング検索ウィンドウの長さでした。 Aho Corsickには this を使用しました

私のテストでは、2つのドキュメントニュースペーパーの記事で1000パターンを検索し、1000回の反復などで平均を求めました。完了までの正規化時間は次のとおりです。

AhoCorsick:1

RabinKarp:1.8

単純検索(各パターンをチェックしてstring.containsを使用):50


*以下の回答で言及されているアルゴを説明するいくつかのリソース:

http://www.seas.gwu.edu/~simhaweb/cs151/lectures/module5/module5.html

http://www.cs.princeton.edu/courses/archive/spr09/cos226/lectures/18SubstringSearch-2x2.pdf

http://www-igm.univ-mlv.fr/~lecroq/string/index.html *

29
Joel

Aho-CorasickアルゴリズムRabin-Karpアルゴリズム を読んでください。

入力が大きすぎず、検索を何度も繰り返したくなく、パターンが少ない場合は、単一のパターンアルゴリズムを数回使用することをお勧めします。 検索アルゴリズムに関するウィキペディアの記事 は、実行時間と前処理時間を含む多くのアルゴリズムを提供します。

実装:

プレゼンテーション:

26

候補文字列のセットを確定的な有限状態オートマトンに変換し、線形時間で入力文字列を実行します。単一の文字列をDFSに変換する方法については、標準的な書籍で詳しく説明されています。文字列のセットを変換するには、まず非決定的オートマトンを作成し、次にそれを決定します。オートマトンのサイズが最悪の場合、指数関数的な爆発を引き起こす可能性がありますが、その後の検索は高速です。特にターゲット文字列が長く、候補が短い場合はうまくいきます。

11
Antti Huima

これが正規表現の目的です。上記のように、有限状態オートマトンが必要ですが、それがまさに標準の正規表現マッチャーの実装方法です。

Javaでは、次のように書くことができます:

StringBuilder sb = new StringBuilder();
bool first = true;
for (String subStr : substrings) {
    if (first)
        first = false;
    else
        sb.append('|');
    sb.append(escape(subStr));
}
Pattern p = Pattern.compile(sb.toString());

メソッドescapeは、正規表現で特別な意味を持つ文字をエスケープする必要があります。

6
Jørgen Fogh

Rabin-Karp複数パターン検索 が最速のようです。

5
emptyset

Aho-Corasickアルゴリズム および関連するアルゴリズムを調べてみてください。私はこれを実装したライブラリーをオフハンドで知りませんが、これはこの問題を解決する古典的な方法です。

2
Avi

Boyer-Mooreアルゴリズム の単一文字列パターンマッチングも確認してください。

2
spoulson

文字列の小さいサイズ(<50文字)を利用して、メモリを犠牲にして、この場合の超高速アルゴを構築できます。

INSTRのすべての可能な部分文字列を、O(n ^ 2)時間を要する1回のハッシュでハッシュできます。次に、CAND文字列の数に関係なく、検索はO(1)になります。非常に多数のCAND文字列に値します。

INSTRが大きい場合は、サフィックス配列を作成して並べ替えることはできません。そのため、一番上の項目が最も長く(= N)、一番下の項目がINSTRの最後の文字になります。次に、各CAND文字列について、length(CAND)<= length(suffix)である限り、先頭からのみ検索します。これらの比較はそれぞれO(n)になります。

2
Joy Dutta
import Java.util.Scanner;

public class StringMatch 
{
    static int temp,i=0,j=0; static boolean flag=true,matcher=false;

    static String str=null,mstr=null;static char astr[],amstr[];

    static void getter(){
        Scanner sc = new Scanner(System.in);
        str = sc.nextLine();
        //String str="today is Monday"; 
        astr=str.toCharArray();
         mstr = sc.nextLine();
        //String mstr="is"; 
         amstr=mstr.toCharArray();
    }

    static void stringMatch(){
        while(i<astr.length){
            if(astr[i]==amstr[j]){
            while((j!=amstr.length)&&flag){temp=i;
                if(astr[i]!=amstr[j]) {flag=false;matcher=false;}
                else{matcher=true;}
                i++;j++;
                //System.out.println(i+"\t"+j);
            }if(matcher==true)break;i=temp;}i++;j=0;flag=true;

        }
        if(matcher==true) {System.out.println("true");}
        else    {System.out.println("false");}
    }

    public static void main(String[] args) {

    StringMatch.getter();
    StringMatch.stringMatch();

    }
}
0
Deepak Kumar

ここ は、Javaの高速文字列検索アルゴリズムの実装です。

0
Mike

別の解決策は、[〜#〜] instr [〜#〜]suffix array を使用することです。
[〜#〜] instr [〜#〜]は小さいため、バブルソートでソートできます。

その後、特定の[〜#〜] cand [〜#〜]文字列をO(logN)で検索できます=時間、
N = length(suffix_array)= length(INSTR)。

0