文字列間の違いをすばやく見つけるためのデータ構造またはアルゴリズム


19

私はすべて長さの100,000文字列の配列を持っています。各文字列を他のすべての文字列と比較して、2つの文字列が1文字異なるかどうかを確認します。現時点では、各文字列を配列に追加するときに、配列内の既存のすべての文字列に対してチェックしています。これは、時間の複雑さを持っています。kn(n1)2k

私がすでにやっていることよりも速く文字列を互いに比較できるデータ構造またはアルゴリズムはありますか?

追加情報:

  • 注文事項は:abcdexbcdeしながら、1つの文字によって異なるabcdeedcba4つの文字で異なります。

  • 1文字異なる文字列のペアごとに、それらの文字列の1つを配列から削除します。

  • 現在、私は1文字だけ異なる文字列を探していますが、その1文字の差を、たとえば2、3、または4文字に増やすことができればいいと思います。ただし、この場合、文字差の制限を大きくする能力よりも効率のほうが重要だと思います。

  • kは通常20〜40の範囲です。


4
1つのエラーで文字列辞書を検索することは、よく知られている問題です。たとえば、cs.nyu.edu
〜adi /

1
20〜40merはかなりのスペースを使用できます。ブルームフィルター(en.wikipedia.org/wiki/Bloom_filter)を見て、縮退した文字列(テストマーの 1つ、2つ、またはそれ以上の置換からのすべてのマーのセット)が「多分」または「明確に」 -not-in "kmersのセット。「maybe-in」を取得した場合は、2つの文字列をさらに比較して、誤検出であるかどうかを判断します。「完全に不一致」の場合は、潜在的な「多分」のヒットのみに比較を制限することにより、必要な文字ごとの比較の総数を減らす真の否定です。
アレックスレイノルズ

より狭い範囲のkで作業している場合、ビットセットを使用して、すべての縮退文字列のブール値のハッシュテーブルを格納できます(たとえば、おもちゃの例ではgithub.com/alexpreynolds/kmer-boolean)。ただし、k = 20-40の場合、ビットセットに必要なスペースは多すぎます。
アレックスレイノルズ

回答:


12

最悪の実行時間を達成することが可能です。O(nklogk)

簡単に始めましょう。すべてではありませんが、多くの入力で効率的である実装しやすいソリューションに関心がある場合は、多くの状況で実際に十分な、シンプルで実用的で実装しやすいソリューションがあります。ただし、最悪の場合は2次実行時間に戻ります。

各文字列を取得し、文字列の前半をキーとするハッシュテーブルに保存します。次に、ハッシュテーブルバケットを反復処理します。同じバケット内の文字列の各ペアについて、それらが1文字で異なるかどうかを確認します(つまり、後半が1文字で異なるかどうかを確認します)。

続いて、各文字列を取り、ハッシュテーブルに格納し、この時間は、上キー文字列の半分。再度、同じバケット内の文字列の各ペアを確認します。

文字列が十分に分散していると仮定すると、実行時間はおそらくます。さらに、1文字異なる文字列のペアが存在する場合、2つのパスのいずれかで検出されます(1文字だけ異なるため、その異なる文字は文字列の前半または後半にある必要があり、そのため、文字列の後半または前半は同じでなければなりません)。ただし、最悪の場合(たとえば、すべての文字列が同じ文字で開始または終了する場合)、これは実行時間に低下するため、最悪の実行時間はブルートの改善にはなりません力。k / 2 O n 2 k O(nk)k/2O(n2k)

パフォーマンスの最適化として、バケットに含まれる文字列が多すぎる場合、同じプロセスを再帰的に繰り返して、1文字異なるペアを探します。再帰呼び出しは、長さ文字列に対して行われます。k/2

最悪の場合の実行時間を気にする場合:

上記のパフォーマンス最適化により、最悪の場合の実行時間はになると思います。O(nklogk)


3
文字列が同じ前半を共有している場合実際の生活で非常によく発生する可能性があります)、複雑さは改善されていません。Ω(n)
アインポクルム-モニカを

@einpoklum、確かに!だから、2番目の文に、最悪の場合は2次実行時間に戻るというステートメントと、最悪の場合の複雑さを実現する方法を説明する最後の文のステートメントを書きました。最悪の場合について。しかし、私はおそらくそれをあまり明確に表現していなかったと思うので、それに応じて答えを編集しました。今はいいですか?O(nklogk)
DW

15

私のソリューションはj_random_hackerに似ていますが、使用するハッシュセットは1つだけです。

文字列のハッシュセットを作成します。入力内の文字列ごとに、セットに文字列を追加します。これらの各文字列では、文字のいずれかを、文字列のいずれにもない特殊文字に置き換えます。それらを追加する間、それらがまだセットにないことを確認してください。その場合、(最大)1文字だけ異なる2つの文字列があります。k

文字列「abc」、「adc」の例

abcには、「* bc」、「a * c」、および「ab *」を追加します

adcには、「* dc」、「a * c」、「ad *」を追加します

2回目に「a * c」を追加すると、すでにセットにあることがわかります。そのため、1文字だけ異なる2つの文字列があることがわかります。

このアルゴリズムの合計実行時間はです。これは、入力のすべての文字列に対して新しい文字列を作成するためです。これらの文字列のそれぞれについて、ハッシュを計算する必要があります。通常、時間かかります。k n O k O(nk2)knO(k)

すべての文字列を保存するには、スペースが必要です。O(nk2)

さらなる改善

変更された文字列を直接保存するのではなく、元の文字列とマスクされている文字のインデックスへの参照を持つオブジェクトを保存することにより、アルゴリズムをさらに改善できます。この方法では、すべての文字列を作成する必要はなく、すべてのオブジェクトを格納するためにスペースのみが必要です。O(nk)

オブジェクトのカスタムハッシュ関数を実装する必要があります。Java実装を例として取り上げることができます。javaのドキュメントを参照してください。java hashCodeは、各文字のUnicode値に乗算します(は文字列の長さ、は文字の1から始まるインデックスです。変更された各文字列は元の文字と1文字だけ異なることに注意してください。ハッシュコードへのその文字の寄与を計算します。代わりにそれを減算し、マスキング文字を追加します。これは計算にを必要とします。これにより、合計実行時間を k i O 1 O n k 31kikiO(1)O(nk)


4
@JollyJokerええ、この方法ではスペースが問題になります。変更された文字列を保存せずに、文字列とマスクされたインデックスへの参照を含むオブジェクトを保存することで、スペースを削減できます。これにより、O(nk)スペースが残ります。
サイモンプリンズ

時間で各文字列のハッシュを計算するには、特別な自家製ハッシュ関数が必要だと思います(たとえば、時間で元の文字列のハッシュを計算し、削除されたそれぞれとXORします)時間ごとの文字(これはおそらく他の点でかなり悪いハッシュ関数です))。ところで、これは私のソリューションにかなり似ていますが、個別のものの代わりに単一のハッシュテーブルを使用し、文字を削除する代わりに「*」に置き換えます。O k O k O 1 kkO(k)O(k)O(1)k
j_random_hacker

@SimonPrins動作する可能性のあるカスタムequalsおよびhashCodeメソッドを使用します。これらのメソッドでa * bスタイルの文字列を作成するだけで、安全になります。ここでの他の答えのいくつかはハッシュ衝突の問題があると思う。
JollyJoker

1
@DWハッシュの計算に時間かかるという事実を反映するように投稿を変更し、合計実行時間をO n k )に戻すソリューションを追加しました。O(k)O(nk)
サイモンプリンズ

1
@SimonPrins最悪の場合、ハッシュが衝突したときにhashset.containsで文字列の等価性チェックが行われるため、nk ^ 2になる可能性があります。もちろん、最悪の場合は、すべての文字列がために同じハッシュを取得するために、特に、文字列のほとんど手作りセットが必要になるとまったく同じハッシュを有する場合で*bca*cab*。私はそれが不可能と示されることができたのだろうか?
ジョリージョーカー

7

私はハッシュテーブルH 1H kを作成し、各キーはk 1 長の文字列をキーとして、数値のリスト(文字列ID)を値として持ちます。ハッシュテーブルH iには、これまでに処理されたすべての文字列が含まれますが、位置iの文字は削除されます。たとえば、k = 6の場合、H 3 [ A B D E F ]には、これまでに見られたパターンAを持つすべての文字列のリストが含まれますkH1,,Hk(k1)Hiik=6H3[ABDEF]手段は"任意の文字"。次に、 j番目の入力文字列 s jを処理します。ABDEFjsj

  1. 1からkの範囲の各について: ik
    • s jからi番目の文字を削除して、文字列形成します。sjisj
    • 見上げる。ここでのすべての文字列IDは、sと等しいか、位置iのみが異なる元の文字列を識別します。これらを文字列s jの一致として出力します。(正確な重複を除外したい場合は、ハッシュテーブルの値型を(文字列ID、削除された文字)ペアにして、s jから削除したのと同じ文字が削除されたものをテストできるようにします。)Hi[sj]sisjsj
    • 将来使用するクエリのためにH iに挿入します。jHi

各ハッシュキーを明示的に保存する場合、スペースを使用する必要があるため、少なくともその時間の複雑さがあります。しかし、Simon Prinsによって説明されているように、特定の文字列のすべてのkハッシュキーが必要とするような方法で、文字列への一連の変更を表すことができますO K 空間につながるO N 、K 全体的な空間、及び可能性開口O n個のkはO(nk2)*kO(k)O(nk)O(nk)時間も。この時間の複雑さを達成するには、O k 時間で長さkの文字列のすべてのバリエーションのハッシュを計算する方法が必要です:たとえば、これはDWによって示唆されるように多項式ハッシュを使用して実行できます(これは削除された文字と元の文字列のハッシュを単純にXORするよりもはるかに優れています)。kkO(k)

Simon Prinsの暗黙的な表現トリックは、各文字の「削除」が実際に実行されないことも意味します。したがって、パフォーマンスのペナルティなしで、通常の配列ベースの文字列表現を使用できます。


2
いい解決策。適切な特注のハッシュ関数の例は、多項式ハッシュです。
DW

@DWに感謝します。「多項式ハッシュ」の意味を少し明確にできますか この用語をグーグルで調べても、決定的なものは何も得られませんでした。(必要に応じて私の投稿を直接編集してください。)
j_random_hacker

1
文字列をpを法とするベース数として読み取ります。ここで、pはハッシュマップサイズよりも小さい素数で、qpのプリミティブルートであり、qはアルファベットサイズより大きくなります。「多項式ハッシュ」と呼ばれるのは、係数がqの文字列で与えられる多項式を評価するようなものだからです。O k 時間で必要なハッシュをすべて計算する方法を理解するための演習として残しておきます。このアプローチは、希望する条件を満たすp qの両方をランダムに選択しない限り、敵に影響されないことに注意してください。qppqpqqO(k)p,q
user21820

1
このソリューションは、一度にk個のハッシュテーブルのうちの1つのみが存在する必要があることを観察することにより、さらに洗練され、メモリ要件を削減できると思います。
マイケルケイ

1
@MichaelKay:O k 時間で文字列の可能な変更のハッシュを計算する場合、それは機能しません。まだどこかに保存する必要があります。したがって、一度に1つの位置のみをチェックする場合、k倍の数のハッシュテーブルエントリを使用してすべての位置を一緒にチェックする場合、k倍の時間がかかります。kO(k)kk
user21820

2

kr1..kM0ri<Mx1..kr 1 .. k k 1 / M O k (i=1kxiri)modMr1..kk明確な文字列の任意のペアの衝突の最大確率が急速に増加します。また、時間で、1文字が変更された各文字列のすべての可能なハッシュを計算する方法も明らかです。1/MO(k)

本当に均一なハッシュを保証したい場合は、からまでのと各文字各ペアに対してより小さいランダムな自然数 1つ生成してから、各文字列をハッシュできますに。その場合、特定の文字列の任意のペアが衝突する確率は、正確にです。この方法は、文字セットが比べて比較的小さい場合に適しています。M i c i 1 k cr(i,c)M(i,c)i1kck i = 1 r i x imod M 1 / M nx1..k(i=1kr(i,xi))modM1/Mn


2

ここに掲載されているアルゴリズムの多くは、ハッシュテーブルでかなりのスペースを使用しています。補助ストレージランタイムシンプルアルゴリズムを次に示します。O N LG N K 2O(1)O((nlgn)k2)

コツは、を使用することです。これは、2 番目の値と間のコンパレータで、(辞書順)でtrueを返し、番目の文字を無視します。すると、アルゴリズムは次のようになります。a b a < b kCk(a,b)aba<bk

まず、単に文字列を定期的に並べ替え、線形スキャンを実行して重複を削除します。

次に、各:k

  1. コンパレータとしてして文字列をソートします。Ck

  2. のみが異なる文字列が隣接するようになり、線形スキャンで検出できるようになりました。k


1

長さの二つの文字列kは、一つの文字が異なる長さのプレフィックスを共有Lと長さのサフィックスMようにK = L + M + 1

サイモン・プリンスによって答えは、すべての接頭辞/接尾辞の組み合わせ明示的に格納することで、これを符号化し、すなわちabcなり*bca*cab*。それはk = 3、l = 0,1,2およびm = 2,1,0です。

valarMorghulisが指摘しているように、プレフィックスツリーで単語を整理できます。非常によく似た接尾辞ツリーもあります。各プレフィックスまたはサフィックスの下にあるリーフノードの数でツリーを拡張するのはかなり簡単です。これは、新しい単語を挿入するときにO(k)で更新できます。

これらの兄弟カウントを必要とする理由は、新しい単語が与えられると、すべての文字列を同じプレフィックスで列挙するか、すべての文字列を同じサフィックスで列挙するかを知るためです。たとえば、入力としての「abc」の場合、可能な接頭辞は「」、「a」、および「ab」であり、対応する接尾辞は「bc」、「c」、および「」です。明らかなように、短い接尾辞については、接頭辞ツリーの兄弟を列挙する方が適切です。逆も同様です。

@einpoklumが指摘しているように、すべての文字列が同じk / 2プレフィックスを共有する可能性は確かにあります。これはこのアプローチの問題ではありません。プレフィックスツリーは深さk / 2まで線形で、最大k / 2深さまでの各ノードが100.000リーフノードの祖先です。その結果、接尾辞ツリーは(k / 2-1)の深さまで使用されます。これは、接頭辞を共有する場合、文字列の接尾辞が異なる必要があるためです。

[編集]最適化として、文字列の最短の一意のプレフィックスを決定すると、1つの異なる文字がある場合、それがプレフィックスの最後の文字である必要があることがわかります。 1つ短いプレフィックスをチェックします。したがって、「abcde」に最短の一意のプレフィックス「abc」がある場合、「ab?」で始まる他の文字列があることを意味します。「abc」ではありません。つまり、1文字だけが異なる場合、それはその3番目の文字になります。もう「abc?e」を確認する必要はありません。

同じロジックで、「cde」が一意の最短接尾辞であることがわかった場合、長さ1または3の接頭辞ではなく、長さ2の「ab」接頭辞のみをチェックする必要があることがわかります。

この方法は、正確に1文字の違いに対してのみ機能し、2文字の違いには一般化されないことに注意してください。1つの1文字が同一のプレフィックスと同一のサフィックスを区別することに依存します。


あなたは、各文字列のことを示唆していると各1 I K、我々はノードを見つけるP [ S 1... sはI - 1 ]長さ-に対応するI - 1 接頭辞トライで接頭辞、およびノードS [ s i + 1s k ]は長さに対応します- k i 1 s1ikP[s1,,si1](i1)S[si+1,,sk](ki1)サフィックストライのサフィックス(それぞれが償却時間かかります)、それぞれの子孫の数を比較し、子孫の少ない方を選択して、そのトライの残りの文字列を「プローブ」しますか?O(1)
j_random_hacker

1
アプローチの実行時間はどのくらいですか?最悪の場合、二次的である可能性があります。すべての文字列が同じ文字で開始および終了するとどうなるか考えてください。k/4
DW

最適化のアイデアは賢く、興味深いものです。mtachesのチェックを行う特定の方法を念頭に置いていましたか?「abcde」に一意の最短プレフィックス「abc」がある場合、「ab?de」という形式の他の文字列をチェックする必要があることを意味します。それを行うための特定の方法を念頭に置いていましたか?それは効率的ですか?結果の実行時間はどのくらいですか?
DW

@DW:「ab?de」という形式の文字列を見つけるには、「ab」の下に存在するリーフノードの数をプレフィックスツリーで確認し、「de」の下に存在するノードの数をサフィックスツリーで確認してから、列挙する2つの最小値。すべての文字列が同じk / 4文字で始まり、終わるとき; つまり、両方のツリーの最初のk / 4ノードにはそれぞれ1つの子があります。そして、はい、それらの木が必要になるたびに、それらはO(n * k)ステップである横断されなければなりません。
–MSalters

プレフィックスtrieで「ab?de」という形式の文字列を確認するには、「ab」のノードに到達し、その子ごとに、パス「de」がvの下に存在するかどうかを確認します。つまり、これらのサブクエリ内の他のノードを列挙する必要はありません。これは、かかるO H 時間、アルファベットのサイズであり、Hはトライにおける初期のノードの高さを。 hO k )です。したがって、アルファベットサイズがO n )の場合、実際にはO n k )です。vvO(ah)ahhO(k)O(n)O(nk)全体的な時間ですが、小さいアルファベットが一般的です。子の数(子孫ではない)と高さは重要です。
j_random_hacker

1

バケットに文字列を保存するのは良い方法です(これを概説するさまざまな答えがすでにあります)。

別の解決策は、ソートされたリストに文字列を保存することです。秘Theは、場所依存のハッシュアルゴリズムでソートすることです。これは、入力が類似している場合に同様の結果を生成するハッシュアルゴリズムです[1]。

あなたは、文字列を調査したいたびに、そのハッシュを計算し、(取って、あなたのソートされたリストにそのハッシュの位置を検索できの配列またはのためのO n個のリンクされたリストのため)。その位置の隣人(+/- 1のインデックスを持つものだけでなく、すべての近くの隣人を考慮する)が似ている(1文字ずれている)場合は、一致を見つけました。全く同様の文字列が存在しない場合は、(取るあなたが見つけた位置に新しい文字列を挿入することができますO 1 連結リストとのためにO n個の配列のため)。O(log(n))O(n)O(1)O(n)

可能性のあるローカリティ依存ハッシュアルゴリズムの1つは、Nilsimsa(たとえばpythonで利用可能なオープンソース実装)です。

[1]:多くの場合、SHA1のようなハッシュアルゴリズムは反対の目的で設計されていることに注意してください。入力が似ているが等しくない場合、ハッシュが大きく異なることに注意してください。

免責事項:正直に言うと、私は実稼働アプリケーション用にネストされた/ツリー構成のバケットソリューションの1つを個人的に実装します。しかし、ソートされたリストのアイデアは、興味深い代替手段として私を驚かせました。このアルゴリズムは、選択したハッシュアルゴリズムに大きく依存することに注意してください。Nilsimsaは私が見つけたアルゴリズムの1つです。ただし、もっとたくさんあります(たとえば、TLSH、Ssdeep、Sdhash)。Nilsimsaが私の説明したアルゴリズムで動作することを確認していません。


1
興味深いアイデアですが、入力が1文字だけ異なる場合に2つのハッシュ値がどれだけ離れているかについて、いくつかの境界が必要になると思います-隣人ではなく、そのハッシュ値の範囲内のすべてをスキャンします。(1文字異なる文字列のすべての可能なペアに対して隣接するハッシュ値を生成するハッシュ関数を持つことは不可能です。バイナリアルファベットの長さ2の文字列を考えます:00、01、10、11。h(00)がh(10)とh(01)の両方に隣接している場合、それらの間になければなりません。その場合、h(11)は両方に隣接できません。逆も同様です。)
j_random_hacker

隣人を見るだけでは十分ではありません。リストabcd、acef、agcdを検討してください。一致するペアは存在しますが、abcdはagcdの隣人ではないため、プロシージャはそれを検出しません。
DW

あなたは両方とも正しいです!隣人とは、「直接の隣人」だけを意味するのではなく、「近隣」の近接した地位を考えました。ハッシュアルゴリズムに依存するので、何人のネイバーを調べる必要があるかを指定しませんでした。しかし、あなたは正しいです、私はおそらく私の答えでこれを書き留めるべきです。ありがとう:)
tessi

1
「LSH ...類似のアイテムは同じ「バケット」に高い確率でマッピングされます」-確率アルゴリズムであるため、結果は保証されません。したがって、100%のソリューションが必要か、99.9%で十分かはTSに依存します。
ブラット

1

O(nk+n2)O(nk)

  1. nX=x1.x2.x3....xnxi,1inX

  2. xi(i1)kxixjj<ixjxi=xjxi[p]xj[p]xjxixj

    for (i=2; i<= n; ++i){
        i_pos = (i-1)k;
        for (j=1; j < i; ++j){
            j_pos = (j-1)k;
            lcp_len = LCP (i_pos, j_pos);
            if (lcp_len < k) { // mismatch
                if (lcp_len == k-1) { // mismatch at the last position
                // Output the pair (i, j)
                }
                else {
                  second_lcp_len = LCP (i_pos+lcp_len+1, j_pos+lcp_len+1);
                  if (lcp_len+second_lcp_len>=k-1) { // second lcp goes beyond
                    // Output the pair(i, j)
                  }
                }
            }
        }
    }
    

SDSLライブラリを使用して、サフィックス配列を圧縮形式で構築し、LCPクエリに応答できます。

XO(nk)O(n2)

O(nk+qn2)q

j<ij


O(kn2)k

O(nk+n2)O(kn2)O(1)

私のポイントは、質問の著者にとってk = 20..40であり、そのような小さな文字列の比較に必要なCPUサイクルはわずかであるため、ブルートフォースとアプローチの実際の違いはおそらく存在しないことです。
ブラット

1

O(nk)**bcdea*cde

このアプローチを使用して、複数のCPU / GPUコア間で作業を分割することもできます。


n=100,000k40O(nk)

0

これは、ハッシュを含まない@SimonPrinsの回答の短いバージョンです。

文字列にアスタリスクが含まれていないと仮定します。

  1. nkkO(nk2)
  2. O(nk2lognk)
  3. O(nk2)

Pythonでハッシュを暗黙的に使用する代替ソリューション(美しさに抵抗することはできません):

def has_almost_repeats(strings,k):
    variations = [s[:i-1]+'*'+s[i+1:] for s in strings for i in range(k)]
    return len(set(variations))==k*len(strings)

kO(nk)

O(n2)

0

2+の不一致ファインダーに関する私の見解を以下に示します。この投稿では、各文字列が円形であり、インデックスの長さ2のfe部分文字列k-1は、シンボルとstr[k-1]それに続くで構成されることに注意してくださいstr[0]。そして、インデックスの長さ2の部分文字列は-1同じです!

Mkmlen(k,M)=k/M1Mk=20M=4abcd*efgh*ijkl*mnop*

次に、Mシンボルの文字列間でkシンボルまでのすべての不一致を検索するアルゴリズム:

  • 0からk-1までの各iに対して
    • グループにすべての文字列を分割しstr[i..i+L-1]L = mlen(k,M)。FeがL=44つのシンボル(DNAから)のみのアルファベットを持っている場合、これにより256のグループが作成されます。
    • 最大100個の文字列より小さいグループは、ブルートフォースアルゴリズムでチェックできます。
    • より大きなグループの場合、二次分割を実行する必要があります。
      • Lすでに一致したグループシンボルのすべての文字列から削除する
      • i-L + 1からkL-1の各jに対して
        • グループにすべての文字列を分割しstr[i..i+L1-1]L1 = mlen(k-L,M)。Fe if k=20, M=4, alphabet of 4 symbols、so L=4およびL1=3、これにより64グループが作成されます。
        • 残りは読者のための練習問題として残されています:D

なぜj0から始めないのですか?これらのグループは同じ値ですでに作成されているためij<=i-Lwithはiとjの値を入れ替えたjobとまったく同じになります。

さらなる最適化:

  • すべての位置で、文字列も考慮しますstr[i..i+L-2] & str[i+L]。これにより、作成されるジョブの量は2倍になりますがL、1ずつ増やすことができます(数学が正しければ)。したがって、256個のグループではなくfeで、データを1024個のグループに分割します。
  • L[i]*0..k-1M-1k-1

0

私はアルゴの発明と最適化に日々取り組んでいるので、パフォーマンスのすべての最後のビットが必要な場合、それは計画です:

  • 確認してください*つまり、代わりに単一のジョブの処理を、独立してそれぞれの位置でn*k開始-バリアント文字列をk独立した仕事に、各チェックn文字列を。これらのkジョブを複数のCPU / GPUコアに分散できます。これは、2文字以上のdiffをチェックする場合に特に重要です。ジョブサイズを小さくすると、キャッシュの局所性も向上し、それだけでプログラムが10倍高速になります。
  • ハッシュテーブルを使用する場合は、線形プローブと最大50%の負荷係数を使用する独自の実装を使用します。それは高速で実装が非常に簡単です。または、オープンアドレッシングで既存の実装を使用します。STLハッシュテーブルは、個別のチェーンを使用するため低速です。
  • @AlexReynoldsによって提案されているように、3状態ブルームフィルター(0/1/1 +の出現を区別する)を使用してデータを事前フィルター処理することができます。
  • 0からk-1までの各iに対して、次のジョブを実行します。
    • 各文字列(*i番目の位置)と文字列インデックスの4〜5バイトのハッシュを含む8バイトの構造体を生成し、それらを並べ替えるか、これらのレコードからハッシュテーブルを構築します。

並べ替えには、次の組み合わせを試してください。

  • 最初のパスは、TLBトリックを使用した64〜256の方法でのMSD基数ソートです。
  • 2番目のパスは、TLBトリックなしの256〜1024ウェイのMSD基数ソートです(合計64Kウェイ)
  • 3番目のパスは、残りの不整合を修正するための挿入ソートです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.