文字列内の部分文字列を検索するために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
Aho-Corasickアルゴリズム と Rabin-Karpアルゴリズム を読んでください。
入力が大きすぎず、検索を何度も繰り返したくなく、パターンが少ない場合は、単一のパターンアルゴリズムを数回使用することをお勧めします。 検索アルゴリズムに関するウィキペディアの記事 は、実行時間と前処理時間を含む多くのアルゴリズムを提供します。
実装:
プレゼンテーション:
候補文字列のセットを確定的な有限状態オートマトンに変換し、線形時間で入力文字列を実行します。単一の文字列をDFSに変換する方法については、標準的な書籍で詳しく説明されています。文字列のセットを変換するには、まず非決定的オートマトンを作成し、次にそれを決定します。オートマトンのサイズが最悪の場合、指数関数的な爆発を引き起こす可能性がありますが、その後の検索は高速です。特にターゲット文字列が長く、候補が短い場合はうまくいきます。
これが正規表現の目的です。上記のように、有限状態オートマトンが必要ですが、それがまさに標準の正規表現マッチャーの実装方法です。
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
は、正規表現で特別な意味を持つ文字をエスケープする必要があります。
Rabin-Karp複数パターン検索 が最速のようです。
Aho-Corasickアルゴリズム および関連するアルゴリズムを調べてみてください。私はこれを実装したライブラリーをオフハンドで知りませんが、これはこの問題を解決する古典的な方法です。
Boyer-Mooreアルゴリズム の単一文字列パターンマッチングも確認してください。
文字列の小さいサイズ(<50文字)を利用して、メモリを犠牲にして、この場合の超高速アルゴを構築できます。
INSTRのすべての可能な部分文字列を、O(n ^ 2)時間を要する1回のハッシュでハッシュできます。次に、CAND文字列の数に関係なく、検索はO(1)になります。非常に多数のCAND文字列に値します。
INSTRが大きい場合は、サフィックス配列を作成して並べ替えることはできません。そのため、一番上の項目が最も長く(= N)、一番下の項目がINSTRの最後の文字になります。次に、各CAND文字列について、length(CAND)<= length(suffix)である限り、先頭からのみ検索します。これらの比較はそれぞれO(n)になります。
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();
}
}
ここ は、Javaの高速文字列検索アルゴリズムの実装です。
別の解決策は、[〜#〜] instr [〜#〜]に suffix array を使用することです。
[〜#〜] instr [〜#〜]は小さいため、バブルソートでソートできます。
その後、特定の[〜#〜] cand [〜#〜]文字列をO(logN)で検索できます=時間、
N = length(suffix_array)= length(INSTR)。