データアーキテクチャとアルゴリズム-グラフィカルKMPアルゴリズム(調査の概要、文字列)[推奨コレクション]

はじめに:KMPアルゴリズムを少し前に学んだので、少し複雑に感じますが、とにかく理解しているので、後で思い出せるように記録してください。

1.はじめに

まず、例を見てみましょう。AとBの2つの文字列があります。AにBがありますか?説明を簡単にするために、最初に2つの文字列
A = "abcaabababaa"
B = "abab"の値を指定し
ます。では、通常のマッチングはどのように機能しますか?
もちろん、一つ一つです。(青い色は一致したことを示し、黒い色は一致が失敗したことを示します。)
ここに画像の説明を挿入
しかし、そのような一致は無駄であることがわかります!
なぜそう言うのですか?4番目のステップが表示されます。4
ここに画像の説明を挿入
番目のステップでは、 3番目にcとaが見つかりました一致しません。次に、5番目のステップで、B文字列を1ビット戻し、最初の文字列から一致を開始します。
ここに画像の説明を挿入
以前の照合結果によると、B文字列の最初の2桁がabであることがわかっているため、既知の情報が大幅に浪費されています。どのように移動しても、bと照合できないため、ペアを直接スキップする必要があります。 Aストリングの2番目の位置の一致は、Aストリングの3番目の位置と同じです。

おそらく、この例は十分に古典的ではないので、別の例を挙げましょう。

A = "abbaabbbabaa"
B = "abbaaba"

この例では、最初の位置から一致が失敗するまで一致します。

abbaabbbabba
abbaaba
7番目の位置が一致しないことがわかった
ので、元の方法で一致し続けると、文字列Bを1つ戻し、最初の文字から
abbaabbbabbaの一致を再開します
。_abbaabaが
まだ一致しない場合は、その後、少し後退し続ける必要があります。
住む!
最初の6桁が一致したので、A文字列のこれらの6桁がB文字列の最初の6桁と一致することもわかりました。この情報を使用して、一致を最適化できますか?
言い換えると、上記の一致が失敗した後、
abbaabbbabba
____abbaabaに直接ジャンプして
、不要な一致をたくさん保存することができますか。

編集者は私自身のlinuxC / C ++言語技術交換グループを推奨しています:[ 1106675687 ]グループファイルで共有する方が良いと思う学習本とビデオ資料をいくつかまとめました。必要に応じて追加できます。
ここに画像の説明を挿入

2、KMPアルゴリズム

KMPアルゴリズムは上記の問題を解決します。それについて説明する前に、まず2つの概念を示しましょう。

前缀:指的是字符串的子串中从原串最前面开始的子串,如abcdef的前缀有:a,ab,abc,abcd,abcde
后缀:指的是字符串的子串中在原串结尾处结尾的子串,如abcdef的后缀有:f,ef,def,cdef,bcdef

KMPアルゴリズムはF配列を導入します(多くの記事では次に呼び出されますが、作成者はFを使用することに慣れています。これは、表現するのに便利です)。F[i]は、iより前の文字で構成される最長の同じ部分文字列を表します。接頭辞接尾辞の長さ!
それを理解する方法は?
たとえば、文字列aababaabaの同じプレフィックスサフィックスにはaとaabaがあり、最も長いものはaabaです。

3.KMPアルゴリズムの理解不能性とこの記事で説明されている規則

説明を続ける前に、著者はまず、KMPアルゴリズムがよく理解されていない理由について説明します。
インターネット上にはKMPアルゴリズムに関するブログやチュートリアルがたくさんありますが、著者は多くの情報を参考にしており、プロセスや原則の詳細な説明はあまりありません。本当によく書かれた記事の定義には微妙な違いがあります。 (もちろん、本当によく書かれた記事もあります。ここではすべてをリストしません)。たとえば、一部のラベルは1から始まり、次は前のラベルを意味し、一部は現在のラベルを意味します。それは必然的に混乱するでしょう。
それで、読者が前の研究の著者と同じ混乱を感じるのを防ぐために、ここにいくつかの説明と慣習があります。

1.この記事では、すべての文字列に0から始まる番号が付けられています
。2。この記事では、F配列(つまり、他の記事の次の記事)であるF [i]は、0から0までの文字列の最長の同一プレフィックスサフィックスの長さを表します。私。

第四に、F配列の使用

では、Fのすべての値を取得したとすると、F配列を使用してそれを解決するにはどうすればよいですか?
最初に例を挙げましょう(著者はこのより典型的な例を作成するのに長い時間がかかりました):
A = "abaabaabbabaaabaabbabaab"
B = "abaabbabaab"
もちろん、読者は1つの場所だけが
abaabaabbabaaabaabbabaabに一致することを手動でシミュレート
でき、値を計算することもできます手動シミュレーションによる各Fの

B="a b a a b b a b a a b "
F= 0 0 1 1 2 0 1 2 3 4 52017.7.25 Update 这里之前有一个错误,感谢@ 歌古道指正)(2017.7.2

次に、iを使用して現在の文字列Aが一致する(つまり、まだ一致していない)位置を表し、jを使用して現在の文字列Bが一致する(これもまだ一致していない)位置を表します。 > 0の場合、i-1がすでに一致していることを意味します。はい(jと同じ)。
まず、0からマッチングを開始します。
ここに画像の説明を挿入
この時点で、Aの5番目の位置とBの5番目の位置が一致していないことがわかります(番号付けは0から始まることに注意してください)。この時点でi = 5、j = 5の場合、F [j-1]の値を確認します。

F[5-1]=2;

この手段最初の2桁がすでに一致しているので、私たちの次のマッチングのみ、B列(で、3番目の文字)の第二の位置から開始する必要があることを、詳細については、画像を参照してください。
ここに画像の説明を挿入
その後、一致し続ける。
ここに画像の説明を挿入
我々 A文字列の13番目のビットがB文字列の10番目のビットと一致しないことを確認します。この時点で、i = 13、j = 10の場合、F [j-1]の値を調べます。

F[10-1]=4

これは B文字列の0ビット3ビットが現在の(i-4)(i-1)と一致していることを示しています。この部分を再一致させる必要はありません。B文字列をBの4番目のビットから後方に移動します。文字列。一致し始める:
ここに画像の説明を挿入
この時点で、A文字列の13番目のビットとB文字列の4番目のビットがまだ一致していないことがわかります。
ここに画像の説明を挿入
このとき、i = 13、j = 1なので、見てみましょう。 F [j-1]の値で:

F[1-1]=0

これは、同じプレフィックスとサフィックスがないことを示しています。B文字列の0番目のビットがA文字列のi番目のビットと一致するまで、B文字列を1つ後ろに移動します(この例では、i = 13)。 )
ここに画像の説明を挿入
しかし、現時点では、B文字列の最初の位置がA文字列の13番目の位置と一致していません。ここに画像の説明を挿入
上記の一致プロセスを繰り返すと、一致が成功していることがわかります。
ここに画像の説明を挿入

それはKMPアルゴリズムのプロセスです。
強調すべきもう1つのポイントは、B文字列を後方に移動すると、実際にはi ++であり、Bを移動せずに一致する場合、コードビハインドに表示されるのはi ++、j ++です。ここに説明があります。

最後に、フルバージョンになります(これらの写真を作成するのに長い時間がかかりました!!!):
ここに画像の説明を挿入

5、Fアレイソリューション

F配列を使用して解く方法を具体的に説明するために多くのスペースが使用されたので、F配列を計算する方法は?激しく解決することはできません。

KMPのもう1つの独創的な部分は、上記で使用した方法を使用してAとBを照合し、F配列を計算します。簡単に言うと、B文字列を使用してB文字列自体を照合します。
もちろん、B string == B stringなので、上記のマッチングを直接押すと意味がないので(もちろん完全にマッチングできます)、ここを変更する必要があります。

上記の部分についてはすでに説明したので、最初にFを計算するコードを指定します。

for (int i=1;i<m;i++)
{
    
    
    int j=F[i-1];
    while ((B[j+1]!=B[i])&&(j>=0))
        j=F[j];
    if (B[j+1]==B[i])
        F[i]=j+1;
    else
        F[i]=-1;
}

決定できる最初のポイントは次のとおりです。

  • 1. F [0] = -1(ここでは0である必要がありますが、範囲外を判断するために、同時に0番目とi番目のビットを判断するために-1に設定します。ここのプログラムで)
  • 2.これは前から後ろへの線形導関数であるため、F [i]を計算するときに、F [0] 〜F [i-1]が計算されていることが保証されます。
  • 3.特定のビットで終わる部分文字列に同じプレフィックスとサフィックスがない場合、このビットのFは-1に設定されます(ここで-1に設定する理由は最初のビットと同じです)

重要!:また、プログラムの便宜上、以下の説明において、F [i] = 0は、最長の同一のプレフィックスサフィックスの長さが1であること、すなわち、実際の最長の同一のプレフィックスサフィックス= F [i] + 1を意味する。(重要なコンテンツを拡大する必要があります)
このように設定するのはなぜですか。現時点では、F [i]はプレフィックスとサフィックスの長さだけでなく、プレフィックスの最後の文字の位置も表します。部分文字列B。

したがって、上記のF値を変更する必要があります(ここでは、「_」を使用して位置合わせを支援します)。

B="a _b a a b _b a b a a b "
F= -1 -1 0 0 1 -1 0 1 2 3 4

次に、Fを解決するという考えは次のとおりであると推測することもできます:iの後にF [i-1]の最長の同一のプレフィックス接尾辞を続けることができるかどうかを確認し、可能であれば直接接続し、そうでない場合は話しましょう以下でそれについて。

例えば:

B = "abaabbabaab"を例にとると、2番目のものが表示されます。

B="a baa b b a b a a b"
F=-1 -1

このとき、このaの前のbのF値は-1であるため、この時点でaをbの後ろに接続することはできません(bの同じ最長プレフィックス接尾辞は0です)。このとき、j =- 1なので、B [j +1]とB [2]、つまりB [0]とB [2]が同じかどうかを判断します。同じであるため、F [2] = j + 1 = 0(最初の0〜2文字の最長の同一プレフィックスサフィックスのプレフィックスの終わりを表すのはB [0]であり、長さは0 + 1 = 1です) 。

3番目のものを見てみましょう:

B="a b a a b b a b a a b"
F=-1 -1 0

最初、j = F [3-1] = 0の場合、B [j + 1 = 1]!= B [i = 3]であることがわかります。したがって、この時点でj = F [j] = -1です。 [j + 1 = 0] == B [i = 3]なので、F [3] = j + 1 = 0です。

最後に、例については、4番目を参照してください

B="a b a a b b a b a a b"
F=-1 -1 0 0

まず、F [4-1] = 0、B [j + 1 = 1] == B [i]であるため、F [i] = j + 1 = 1です。

読者は後者をゆっくりと導き出すように勧められています。もう一度強調しますが、この方法で取得したF値は、最長の同一のプレフィックスサフィックス(0から番号が付けられています)のプレフィックスの終了文字の配列位置です。最長の同一のプレフィックスサフィックスの長さが必要な場合は、出力F [i] +1。

6、コード

F配列を解きます。

for (int i=1;i<m;i++)
{
    
    
    int j=F[i-1];
    while ((B[j+1]!=B[i])&&(j>=0))
        j=F[j];
    if (B[j+1]==B[i])
        F[i]=j+1;
    else
        F[i]=-1;
}

F配列を使用して一致を検索します。一致が見つかるたびに、開始位置を出力します。

while(i <n)
{ if(A [i]
B [j])
{ i ++; j ++; if(j


m)
{ printf( "%d \ n"、i-m + 1); //ここでの出力位置は1からラベル付けされていることに注意してください。ラベル付けされた位置を0から出力する場合は、imにする必要があります。このコード質問をしているときに書かれました。その質問の出力文字列の位置には、ラベルが1から始まる必要があります。この省略を指摘してくれた@Draymonderに感謝します。詳細については、コメント領域を参照してください。

       j=F[j-1]+1;
        }
    }
    else
    {
    
    
        if (j==0)
            i++;
        else
            j=F[j-1]+1;
    }
}

次のコンテンツは2019.4.26で更新されます

現在の独自の書き込み方法を貼り付けますが、ここでは文字列に1からのラベルが付けられており、上記で理解していれば変換は難しくありません。

選択したパスを終了するためにひざまずきます。友よ、世界はますます勢いを増していますが、その時の純粋な夢と感情のために一生懸命働き続けることができれば、他の人が何であれ、私たちは自分の本当の色を保ち続けることができます。

おすすめ

転載: blog.csdn.net/m0_50662680/article/details/113181361