郚分文字列怜玢ず関連事項

こんにちはコミュニティ 最近、Habréで、文字列内の郚分文字列のさたざたな怜玢アルゎリズムに関する優れたレビュヌ蚘事が掲茉されたした。 残念ながら、前述のアルゎリズムの詳现な説明はありたせんでした。 私はこのギャップを埋めお、朜圚的に思い出すこずができるものを少なくずもいく぀か説明するこずにしたした。 研究所からのアルゎリズムのコヌスをただ芚えおいる人は、おそらく自分にずっお新しいものを芋぀けるこずはないでしょう。



たず、「なぜこれが䞀䜓なのか」ずいう質問を避けたいず思いたす。 すべおがすでに曞かれおいたす。」 はい、曞かれおいたす。 しかし、第䞀に、䜿甚するツヌルがその制限をよりよく理解するために䞋䜍レベルでどのように機胜するかを知るこずは有甚です。第二に、ボックスから機胜するstrstr関数が十分でない隣接領域が十分に倧きいです。 第䞉に、運が悪く、モバむルプラットフォヌム甚のランタむムを開発する必芁がある堎合がありたす。それを自分で補足する堎合は、賌読しおいるものを知る方が良いでしょうこれが真空䞭の球状の問題ではないこずを確認するには、wcslenを詊しおください Android NDKのwcsstr。



しかし、怜玢するこずは本圓に䞍可胜ですか



事実は、すべおを「テむクアンドサヌチ」ずしお定匏化する明癜な方法が決しお最も効果的ではないこずであり、このような䜎レベルで比范的頻繁に呌び出される機胜にずっお、これは重芁です。 したがっお、蚈画は次のずおりです。

  1. 問題の説明定矩ず芏則をここにリストしたす。
  2. 「額に」の決定ここでは、それをしない方法ず理由を説明したす。
  3. Z関数郚分文字列怜玢の正しい実装の最も単玔なバヌゞョン。
  4. Knuth-Morris-Prattアルゎリズム別の正しい怜玢オプション。
  5. その他の怜玢タスク詳现な説明なしで簡単に調べたす。





問題の声明



問題の暙準バヌゞョンは次のようになりたす行A  text がありたす。 サブストリングX  パタヌン が含たれおいるかどうかを確認する必芁がありたす。ある堎合は、どこから開始するかを確認したす。 これは、Cでstrstr関数が行うこずずたったく同じです。これに加えお、パタヌンのすべおの出珟を怜玢するように䟝頌するこずもできたす。 明らかに、 Xが Aより長くない堎合にのみ、タスクは意味をなしたす。

詳现な説明を簡単にするため、䞀床にいく぀かの抂念を玹介したす。 文字列ずは、誰もがおそらく理解しおいる-これは文字列であり、空の堎合もありたす。 蚘号 、たたは文字は、 アルファベットず呌ばれる特定のセットに属したす このアルファベットは、䞀般的に蚀えば、垞識的にはアルファベットずは無関係かもしれたせん。 ストリングの長さ | A | -これは明らかにその䞭の文字数です。 ストリング A [ ..i ] のプレフィックスは、ストリング Aの最初のi文字のストリングです。 ストリング A [ j .. ] のサフィックスは 、|のストリングです。 A | -j +最埌の1文字。 Aの郚分文字列はA [ i..j ]ずしお瀺され、 A [ i ]は文字列のi番目の文字です。 空の接尟蟞や接頭蟞などに関する質問 觊れないでください-ロヌカルで察凊するこずは難しくありたせん。 センチネルのようなものがただありたす-アルファベットには芋られないナニヌクなシンボルです。 $蚘号で瀺され、そのような蚘号の付いた有効なアルファベットで補完されたす理論的には、実際には、入力行に衚瀺されない蚘号を芋぀けるよりも、远加のチェックを適甚する方が簡単です。

蚈算では、文字列の最初の䜍眮から文字を考慮したす。 コヌドを曞くのは䌝統的にれロから数えるこずで簡単です。 あるものから別のものぞの移行は難しくありたせん。



額゜リュヌション



盎接怜玢、たたはよく蚀われる「遞択しお怜玢する」は、経隓の浅いプログラマヌの頭に浮かぶ最初の゜リュヌションです。 本質は簡単です。チェックする文字列Aに沿っお進み、その䞭の目的の文字列Xの最初の文字の出珟を探したす。 芋぀かったら、これが同じ望たしい゚ントリであるずいう仮説を立おたす。 次に、テンプレヌトの埌続のすべおの文字を順番にチェックしお、行Aの察応する文字ず䞀臎するかどうかを確認したす。 それらがすべお䞀臎した堎合、これがたさに目の前です。 しかし、文字の1぀が䞀臎しない堎合、仮説をfalseずしお認識する以倖に䜕も残りたせん。これにより、 Xからの最初の文字の出珟に続く文字に戻りたす。

倚くの人々はこの時点で間違えおいたす。戻る必芁はないず信じおいたすが、珟圚の䜍眮から行Aの凊理を続けるこずができたす。 A = “ AAAAB”の怜玢X = “ AAAB”の䟋で瀺すのがそれほど簡単ではない理由。 最初の仮説は、4番目の文字A  "AAAAB"に぀ながり 、そこでミスマッチが芋぀かりたす。 ロヌルバックしない堎合、゚ントリは存圚したすが、芋぀かりたせん。

間違った仮説は避けられたせん。悪い状況䞋でのこのようなロヌルバックのため、 Aの各文字を| X | 回。 ぀たり、蚈算の耇雑さはアルゎリズムO| X || A |の耇雑さです。 したがっお、段萜内のフレヌズの怜玢はドラッグアりトできたす...

公平に蚀うず、行が小さい堎合、そのようなアルゎリズムは、プロセッサの芳点から予枬可胜な動䜜により、「正しい」アルゎリズムよりも速く動䜜するこずに泚意する必芁がありたす。



Z関数



文字列を怜玢する正しい方法のカテゎリの1぀は、ある意味で2぀の文字列の盞関を蚈算するこずです。 たず、2行の先頭を比范するタスクは単玔で簡単であるこずに泚意しおください。矛盟が芋぀かるか、行のいずれかが終わるたで、察応する文字を比范したす。 ストリングAのすべおの接尟郚のセットを怜蚎しおください。 A | .. ] A [ | A | -1 .. ] 、... A [ 1 .. ] 。 行の先頭自䜓ずその接尟蟞のそれぞれを比范したす。 比范は、接尟蟞の最埌に達するか、䞍䞀臎のために䞀郚の蚘号で途切れるこずがありたす。 䞀臎する郚分の長さは、特定の接尟蟞のZ関数のコンポヌネントず呌ばれたす。

぀たり、Z関数は、文字列の最倧の共通プレフィックスずそのサフィックスの長さベクトルです。 うわヌ 誰かを混同したり䞻匵したりする必芁がある堎合には玠晎らしいフレヌズですが、それが䜕であるかを理解するには、䟋を考えた方が良いでしょう。

元の文字列は"ababcaba"です。 各接尟蟞を文字列自䜓ず比范するず、Z関数のプレヌトが埗られたす。

接尟蟞 ひも Z
アババカバ アババカバ -> 8
ババカバ アババカバ -> 0
アブカバ アババカバ -> 2
bcaba アババカバ -> 0
カバ アババカバ -> 0
あば アババカバ -> 3
ba アババカバ -> 0
a アババカバ -> 1




接尟蟞の接頭蟞は郚分文字列にすぎず、Z関数は先頭ず䞭倮で同時に発生する郚分文字列の長さです。 Z関数のコンポヌネントのすべおの倀を考慮するず、いく぀かの芏則性に気付くこずができたす。 たず、Z関数の倀が文字列の長さを超えず、「完党な」接尟蟞A [ 1 .. ]に぀いおのみ䞀臎するこずは明らかですしたがっお、この倀は私たちの興味を匕くものではありたせん。掚論では省略したす。 第二に、単䞀のコピヌの文字列に単䞀の文字がある堎合、それはそれ自䜓ず䞀臎するだけであるため、文字列を2぀の郚分に分割し、Z関数の倀は短い郚分の長さを超えるこずはできたせん。

これらの芳察結果の䜿甚は、次のように提案されおいたす。 「ababcabacab」の行で「abca」を怜玢するずしたす。 これらの行を取り、それらの間にセンチネルを挿入しお連結したす "abca $ababcabacab" 。 このような行では、Z関数のベクトルは次のようになりたす。

a b c a $ a a b a b c a b with a c a b
17 0 0 1 0 2 0 4 0 0 4 0 0 1 0 2 0




完党なサフィックスの倀を砎棄する堎合、センチネルの存圚により、Z iが目的のフラグメントの長さに制限されたす問題の意味では行の半分未満です。 しかし、この最倧倀に達するず、郚分文字列の出珟䜍眮のみで発生したす。 この䟋では、4で目的の行のすべおの発生䜍眮をマヌクしたす 芋぀かったセクションは互いにオヌバヌラップしたすが、掚論はすべお同じであるこずに泚意しおください。

぀たり、Z関数のベクトルをすばやく䜜成し、それを䜿甚しお文字列のすべおの出珟箇所を怜玢できれば、その長さを芋぀けるこずになりたす。 しかし、各サフィックスのZ関数を蚈算する堎合にのみ、これは明らかに「正面」の゜リュヌションよりも高速ではありたせん。 ベクトルの次の芁玠の倀は、前の芁玠に基づいお芋぀けるこずができたす。

䜕らかの方法で、察応するi-1番目の文字たでZ関数の倀を蚈算したずしたす。 Z rをすでに知っおいる特定の䜍眮r <iを考えたす。

したがっお、この䜍眮からのZ r文字は、行の先頭ずたったく同じです。 それらは、いわゆるZブロックを圢成したす。 右端のZブロック、぀たり、最も遠いZブロック最初のブロックはカりントされたせんに興味がありたす。 堎合によっおは、右端のブロックの長さをれロにするこずができたす空でないブロックがi-1をカバヌしない堎合、Z i-1 = 0であっおもi-1stが右端になりたす。

Zブロックレむアりト

このZブロック内の埌続の文字を考慮するず、このサフィックスの䞀郚がすでに行の先頭にあるため、凊理されおいるため、最初から次のサフィックスを比范するこずは意味がありたせん。 Zブロックの最埌たで文字をすぐにスキップできたす。

接尟蟞スキップ

前の結果

぀たり、Z rブロックにあるi番目の文字、぀たり、䜍眮k = i-r + 1の行の先頭にある察応する文字を考慮するず、 関数Z kはすでにわかっおいたす。 Zブロックの最埌たでの距離Z r- irより小さい堎合、このシンボルの䞀臎領域党䜓がr番目のZブロック内にあるこずをすぐに確認できたす。぀たり、結果は次のようになりたす。行の先頭Z i = Z k Z k > = Z r- irの堎合、Z iもZ r- ir以䞊です。 それがどれだけあるかを知るには、Zブロックに続く文字をチェックする必芁がありたす。 さらに、これらの文字のhが行の先頭の察応する文字ず䞀臎する堎合、Z iはhず぀増加したす。Zi = Z k + h。 その結果、新しい右端のZブロックがある堎合がありたすh> 0の堎合。

Zブロックの倖偎

したがっお、右端のZブロックの右偎でシンボルを比范するだけでよく、比范が成功したため、ブロックは右偎に「移動」し、倱敗したブロックはこの䜍眮の蚈算が終了したこずを報告したす。 これにより、文字列の長さに沿っお線圢時間でZ関数のベクトル党䜓を構築できたす。

このアルゎリズムを適甚しお郚分文字列を怜玢するず、時間の耇雑さO| A | + | X |が埗られたす。これは、最初のバヌゞョンの補品よりもはるかに優れおいたす。 確かに、Z関数のベクトルを保存する必芁がありたした。これは、O| A | + | X |の远加メモリを必芁ずしたす。 実際、すべおのオカレンスを怜玢する必芁はないが、1぀だけで十分な堎合、Zブロックの長さを||より倧きくするこずはできないため、O| X |メモリヌを省くこずができたす。 X |、さらに、最初の発生を怜出した埌、行の凊理を続行できたせん。

最埌に、Z関数を蚈算する関数の䟋。 トリックのないモデルバヌゞョンです。

 void z_preprocess(vector<int> & Z, const string & str) { const size_t len = str.size(); Z.clear(); Z.resize(len); if (0 == len) return; Z[0] = len; for (size_t curr = 1, left = 0, right = 1; curr < len; ++curr) { if (curr >= right) { size_t off = 0; while ( curr + off < len && str[curr + off] == str[off] ) ++off; Z[curr] = off; right = curr + Z[curr]; left = curr; } else { const size_t equiv = curr - left; if (Z[equiv] < right - curr) Z[curr] = Z[equiv]; else { size_t off = 0; while ( right + off < len && str[right - curr + off] == str[right + off] ) ++off; Z[curr] = right - curr + off; right += off; left = curr; } } } } 
      





void z_preprocess(vector<int> & Z, const string & str) { const size_t len = str.size(); Z.clear(); Z.resize(len); if (0 == len) return; Z[0] = len; for (size_t curr = 1, left = 0, right = 1; curr < len; ++curr) { if (curr >= right) { size_t off = 0; while ( curr + off < len && str[curr + off] == str[off] ) ++off; Z[curr] = off; right = curr + Z[curr]; left = curr; } else { const size_t equiv = curr - left; if (Z[equiv] < right - curr) Z[curr] = Z[equiv]; else { size_t off = 0; while ( right + off < len && str[right - curr + off] == str[right + off] ) ++off; Z[curr] = right - curr + off; right += off; left = curr; } } } }









Knuth-Morris-PrattアルゎリズムKMP



前の方法の論理的な単玔さにもかかわらず、別のアルゎリズムがより䞀般的であり、ある意味ではZ関数の逆です-Knut-Morris-Pratt KMP アルゎリズム 。 接頭蟞関数の抂念を玹介したす 。 i番目の䜍眮の接頭蟞関数は、iより短く、長さiの接頭蟞の接尟蟞ず䞀臎する最倧行接頭蟞の長さです。 Z関数の定矩が察戊盞手を完党に無効にしなかった堎合、このコンボで間違いなくその堎所に配眮できたす:)そしお、人間の蚀語では次のようになりたす考えられるすべおの行接頭蟞を取埗し、接頭蟞の末尟ずの最長開始䞀臎を調べたす些现な偶然の䞀臎は考慮したせん私自身ず。 「ababcaba」の䟋を次に瀺したす。

プレフィックス プレフィックス p
a a 0
ab ab 0
ab a 孊士 1
ab ab ab ab 2
ababc ababc 0
ababc a ババカ 1
ababc ab アブキャブ 2
ababc aba アバ・バカバ 3




繰り返したすが、プレフィックス関数の倚くのプロパティを芳察したす。 最初に、倀は、定矩から盎接埗られる番号によっお䞊に制限されたす-プレフィックスの長さは、プレフィックス関数よりも倧きくなければなりたせん。 第二に、同様に䞀意の文字は文字列を2぀の郚分に分割し、プレフィックス関数の最倧倀を小さい方の郚分の長さに制限したす-長いものはすべお、他のものず等しくない䞀意の文字を含むためです。

これから、興味のある結論が埗られたす。 この理論的䞊限のある芁玠で達成したず仮定したす。 これは、最初の郚分が最埌の郚分ず䞀臎し、そのうちの1぀が「完党な」半分を衚す、そのようなプレフィックスがここで終了したこずを意味したす。 接頭蟞では、半分が完党に前にある必芁があるこずは明らかです。぀たり、この仮定では短い半分になるはずですが、長い半分では最倧になりたす。

したがっお、前の郚分のように、目的の行ずセンチネルを調べおいる行を連結するず、プレフィックス関数のコンポヌネント内の目的の郚分文字列の長さの゚ントリポむントは、゚ントリが終了する堎所に察応したす。 たずえば、 「ababcabcacab」の行で「abca」を探したす。 「abca $ ababcabcacab」の連結バヌゞョン。 プレフィックス関数は次のようになりたす。

a b c a $ a a b a b c a b with a c a b
0 0 0 1 0 1 2 1 2 3 4 2 3 4 0 1 2




繰り返したすが、郚分文字列のすべおの出珟が䞀気に芋぀かりたした-それらは4で終わりたす。 このプレフィックス関数を効率的に蚈算する方法を理解する必芁がありたす。 アルゎリズムの考え方は、Z関数を構築する考え方ずは少し異なりたす。

KMP

プレフィックス関数の最初の倀は明らかに0です。i番目の䜍眮たでのプレフィックス関数をカりントしたす。 i + 1番目の文字を怜蚎しおください。 i番目の䜍眮の接頭蟞関数の倀がPiの堎合、接頭蟞A [ .. Pi ]は郚分文字列A [ iP i + 1..i ]ず䞀臎したす。 シンボルA [ P i +1 ]がA [ i + 1 ]ず䞀臎する堎合、P i + 1 = P i +1ず安党に曞くこずができたす。 しかし、そうでない堎合、倀はそれより小さくおも同じでもかたいたせん。 もちろん、Pi = 0の堎合、あたり枛少する堎所はないため、この堎合はPi + 1 = 0です。 P i > 0ず仮定したす。 次に、文字列に接頭蟞A [ ..P i ]がありたす。これは、郚分文字列A [ iP i + 1..i ]ず同等です。 必芁なプレフィックス機胜は、これらの同等のセクションず凊理される文字内で圢成されたす。぀たり、プレフィックスの埌の行党䜓を忘れお、このプレフィックスずi + 1番目の文字だけを残すこずができたす。状況は同じです。

KMP

このステップでのタスクは、䞭間が切り取られた行の問題に瞮小されたした A [ ..P i ] A [ i + 1 ] 、これは同じ方法で再垰的に解決できたすただし、末尟再垰はたったく再垰ではなく、ルヌプです。 ぀たり、 A [ P P i +1 ]がA [ i + 1 ]に䞀臎する堎合、P i + 1 = P P i +1になりたす。 䞀臎するか0になるたで、この手順を繰り返したす。

KMP

これらの操䜜を繰り返すず譊告が衚瀺されたす。ネストされた2぀のサむクルが発生しおいるようです。 しかし、これはそうではありたせん。 実際、長さkのネストされたルヌプは、i + 1番目の䜍眮のプレフィックス関数を少なくずもk-1削枛し、プレフィックス関数をそのような倀に増やすには、少なくずもk-1倍以䞊成功する必芁がありたすk-1文字を凊理しお文字を䞀臎させたす。 ぀たり、サむクルの長さはそのようなサむクルの実行間隔に察応するため、アルゎリズムの耇雑さは凊理された文字列の長さに沿っお線圢になりたす。 ここでの状況は、メモリの堎合ずZ関数の堎合ず同じです-線の長さに沿っお線圢ですが、保存する方法がありたす。 さらに、文字が順番に凊理されるずいう䟿利な事実がありたす。぀たり、最初の出珟をすでに受け取っおいる堎合、行党䜓を凊理する必芁はありたせん。

さお、たずえば、コヌドの䞀郚

 void calc_prefix_function(vector<int> & prefix_func, const string & str) { const size_t str_length = str.size(); prefix_func.clear(); prefix_func.resize(str_length); if (0 == str_length) return; prefix_func[0] = 0; for (size_t current = 1; current < str_length; ++current) { size_t matched_prefix = current - 1; size_t candidate = prefix_func[matched_prefix]; while (candidate != 0 && str[current] != str[candidate]) { matched_prefix = prefix_func[matched_prefix] - 1; candidate = prefix_func[matched_prefix]; } if (candidate == 0) prefix_func[current] = str[current] == str[0] ? 1 : 0; else prefix_func[current] = candidate + 1; } } 
      





void calc_prefix_function(vector<int> & prefix_func, const string & str) { const size_t str_length = str.size(); prefix_func.clear(); prefix_func.resize(str_length); if (0 == str_length) return; prefix_func[0] = 0; for (size_t current = 1; current < str_length; ++current) { size_t matched_prefix = current - 1; size_t candidate = prefix_func[matched_prefix]; while (candidate != 0 && str[current] != str[candidate]) { matched_prefix = prefix_func[matched_prefix] - 1; candidate = prefix_func[matched_prefix]; } if (candidate == 0) prefix_func[current] = str[current] == str[0] ? 1 : 0; else prefix_func[current] = candidate + 1; } }







アルゎリズムがより耇雑であるずいう事実にもかかわらず、その実装はZ関数よりもさらに単玔です。



その他の怜玢タスク



その埌、文字列怜玢タスクはこれに限定されず、他のタスクおよび他の゜リュヌションがあるため、興味がない堎合はさらに読むこずはできないずいう手玙がたくさんありたす。 この情報は、単に慣れるためのものであり、必芁に応じお、少なくずも「すべおが私たちの前に盗たれた」こずに泚意し、車茪を再発明するこずはありたせん。

䞊蚘のアルゎリズムは線圢実行時間を保蚌したすが、ボむダヌ・ムヌアアルゎリズムは「デフォルトアルゎリズム 」ずいうタむトルを受け取りたした。 平均するず、線圢時間も埗られたすが、この線圢関数のより良い定数もありたすが、これは平均です。 「悪い」デヌタがありたすが、そのデヌタは単玔な「正面」の比范よりも優れおいたすたあ、qsortず同じように。 それは非垞に玛らわしいので、考慮したせん-ずにかく、もちろん。 自然蚀語のテキスト凊理に焊点を圓お、その最適化においお単語の統蚈的特性に䟝存する倚くの゚キゟチックなアルゎリズムがありたす。

さお、O| X | + | A |の䜕らかの方法で文字列内の郚分文字列を怜玢するアルゎリズムがありたす。 ここで、ゲストブック゚ンゞンを䜜成しおいるずしたす。 犁止されおいるわいせ぀な単語のリストがありたすこれは圹に立たないこずは明らかですが、タスクは単なる䟋です。 メッセヌゞをフィルタリングしたす。 メッセヌゞ内の犁止されおいる各単語を怜玢し、... O| X 1 | + | X 2 | + ... + | X n | + n | A |が必芁です。 どういうわけか、特に「偉倧で力匷い」の「匷倧な衚珟」の蟞曞が非垞に「匷倧」である堎合。 この堎合、怜玢文字列の蟞曞を前凊理しお、怜玢がO| X 1 | + | X 2 | + ... + | X n | + | A |のみを占めるようにする方法がありたす。メッセヌゞが長い堎合。

このような前凊理は、蟞曞からトラむを構築するこずになりたす。ツリヌはダミヌのルヌトから始たり、ノヌドは蟞曞の単語の文字に察応し、ツリヌノヌドの深さは単語の文字の数に察応したす。 蟞曞から単語が終わるノヌドはタヌミナルず呌ばれ、䜕らかの方法でマヌクされたす図の赀。

トラむ

結果のツリヌは、KMPアルゎリズムのプレフィックス関数に類䌌しおいたす。 これを䜿甚するず、蟞曞句内のすべおの単語の出珟箇所をすべお怜玢できたす。 ツリヌを調べお、ツリヌノヌドの圢で次の文字の存圚を確認し、同時に終端の頂点をマヌクする必芁がありたす-これらは単語の出珟です。 ツリヌに察応するノヌドがない堎合、ILCの堎合ず同様に、特別なリンクを䜿甚しおツリヌの䞊䜍でロヌルバックが発生したす。 このアルゎリズムは、Aho-Korasikアルゎリズムず呌ばれたす 。 同じスキヌムを䜿甚しお、入力時に怜玢し、電子蟞曞の次の文字を予枬できたす。

この䟋では、ボロンの䜜成は簡単です。ボロンに順番に単語を远加するだけです「キックバック」甚の远加リンクのみのニュアンス。 このツリヌのメモリ䜿甚量を枛らすこずを目的ずした最適化がいく぀かありたすいわゆるボロン圧瞮-分岐せずにセクションをスキップする。 実際には、これらの最適化はほが必須です。 このアルゎリズムの欠点は、アルファベット順の䟝存性です。ノヌドず占有メモリの凊理時間は、朜圚的な子の数に䟝存し、これはアルファベットのサむズに等しくなりたす。 倧きなアルファベットの堎合、これは深刻な問題ですUnicode文字のセットを想像できたすか。 このすべおに぀いお、このhabratopikaで、たたはGoogleむンデックスを䜿甚しお詳现を読むこずができたす-この問題に関する倚くの情報がありたす。

次に、別のタスクを芋おみたしょう。 前のもので、埌で来るデヌタで䜕を芋぀ける必芁があるかを事前に知っおいた堎合、ここではたったく逆です事前に圌らが怜玢する行が䞎えられたしたが、怜玢するものは䞍明ですが、圌らは倚くを怜玢したす 兞型的な䟋は怜玢゚ンゞンです。 単語が怜玢されるドキュメントは事前に知られおいたすが、そこで怜玢される単語は倖出先に散らばっおいたす。 ここでも、O| X 1 | + | X 2 | + ... + | X n | + n | A |の代わりにO| X 1 | + | X 2 | + ... + | X n | + | A |

既存の文字列のすべおの可胜な接尟蟞が存圚するホり玠を構築するこずが提案されおいたす。 次に、テンプレヌトの怜玢は、目的のテンプレヌトに察応するツリヌ内のパスの存圚をチェックするこずになりたす。 すべおの接尟蟞を培底的に怜玢しおこのようなホり玠を䜜成する堎合、この手順にはO| A | 2 時間ず倧量のメモリが必芁になりたす。 しかし、幞いなこずに、圧瞮ツリヌでそのようなツリヌをすぐに構築できるアルゎリズムがありたす- サフィックスツリヌ 、そしおO| A |でそれを行いたす。 最近Habréでこれに関する蚘事がありたしたので、興味のある人はそこでUkkonenアルゎリズムに぀いお読むこずができたす。

通垞、接尟蟞ツリヌには悪い点が2぀ありたす。それはツリヌであるずいうこずず、ツリヌのノヌドがアルファベット順に䟝存しおいるずいうこずです。 サフィックス配列はこれらの欠点を免れおいたす。 接尟蟞配列の本質は、文字列のすべおの接尟蟞が䞊べ替えられおいる堎合、郚分文字列の怜玢は、目的のパタヌンの最初の文字で隣接する接尟蟞のグルヌプを怜玢し、埌続のものの範囲をさらに絞り蟌むこずになりたす。 同時に、サフィックス自䜓を゜ヌトされた圢匏で保存する必芁はありたせん;゜ヌスデヌタでサフィックスが始たる䜍眮を保存するだけで十分です。 確かに、この構造の時間䟝存性はわずかに悪くなりたす。1回の怜玢では、考えおすべおを慎重に行うずO| X | + log | A |かかり、気にしない堎合はO| X | log | A |です。 比范のために、固定アルファベットO| X |のツリヌで。 ただし、これがツリヌではなく配列であるずいう事実は、メモリキャッシングの状況を改善し、プロセッサ遷移予枬のタスクを容易にしたす。 サフィックス配列は、KÀrkkÀinen-Sandersアルゎリズムを䜿甚しお線圢時間で構築されたすすみたせんが、ロシア語でどのように聞こえるかはほずんどわかりたせん。 今日、これは最も人気のある行のむンデックス付け方法の1぀です。

ここでは、近䌌文字列怜玢ず類䌌床の分析の質問には觊れたせん。これは、この蚘事に詰め蟌むには広すぎる領域です。 私はただ人々がそこでパンを䜕もせずに食べず、倚くの異なるアプロヌチを考え出したので、あなたが同様の問題に遭遇したら、芋぀けお読んでください。 おそらく、この問題はすでに解決されおいたす。



読んでくれた人に感謝したす そしお、これを読んだ人に、どうもありがずう



UPDホり玠に関する重芁な蚘事ぞのリンクを远加したしたレむであり、プレフィックスツリヌであり、ロヌドされたツリヌであり、トラむです。



All Articles