次の配列の KMP アルゴリズム

1 はじめに

\text{KMP}KMP アルゴリズムと呼ばれる Knuth-Morris-Pratt アルゴリズムは、\text{Donald Knuth}Donald Knuth、\text{James H. Morris}James H. Morris および \text{Vaughan Pratt} によって開発されました。ヴォーン・プラット 1977 年 1977 年に共同出版。これは、文字列一致アルゴリズムを解決するためによく使用されます。

ブルート フォース マッチング アルゴリズムの場合、パターン文字列の針は、一致するメイン文字列干し草の山にある長さ mm のすべての部分文字列と 1 回一致します。これによるオーバーヘッドは膨大で、時間の複雑さは O(m*n)、空間の複雑さは O(1) です。

パターン文字列と照合する文字列が一致しない場合、力ずくの照合アルゴリズムでは、次のステップはパターン文字列を完全にロールバックしてから再照合することですこれには単純で理解しやすいという利点がありますが、一致した文字で利用可能な情報を完全に破棄します

KMP アルゴリズムのコアは、現在の位置が一致しない場合にロールバックする必要があるステップ数を格納する次の配列にあり、既存の情報を効果的に利用します。その時間計算量は O(n+m) で、空間計算量は O(m) です。

2. コード

次にシークする原理を理解するのは難しそうに見えますが、そのコードは非常に簡潔で、数行しかありません。

int GetNext(char ch[], int length, int next[]){
    next[1] = 0;
    int i = 1, j = 0;
    while(i <= length){
        if(j==0 || ch[i] == ch[j]){
            next[++i] = ++j;
        }else{
            j = next[j];
        }
    }
}

2.1 原則

2.2.1 プレフィックスとサフィックス

不一致がある場合にどれだけバックオフするかという問題については、プレフィックスとサフィックスの概念を理解する必要があります。矢印が示す位置が現在一致しており、A と B が等しくないことが判明した場合、力ずくの方法で矢印をパターン文字列 (黄色の文字列) A の先頭に移動する必要があります。

 実際、矢印の前にある文字列ヘッダーとテール (2 つの白いボックス) が同じであることがわかったので、実際には、矢印を文字列ヘッダーの同じ白いボックスに移動することで、引き続き一致させることができます。接頭辞および接尾辞と呼ばれます

 この動画では、アルゴリズム「Tianqin Open Class」の詳細な説明があります KMP アルゴリズムのわかりやすいバージョン_哔哩哔哩_bilibili

2.2.2 次のコードの説明を探す

次の配列は、現在の最長の共通部分文字列 (つまり、最長のプレフィックス) に 1 を加えた長さを記録することです。

next[1]=0;

1つ目はパターン文字列の最初の文字で、前に文字がないので接頭辞は0ですが、最初の文字の接頭辞を-1として記録し、1つ足してから次の [1] = 0 を取得します。後続のプレフィックスは 0 で、これは 0 + 1 と見なされます。つまり、0 は文字列の先頭にのみ表示されます。

int i = 1, j = 0;

i は、現在の位置のパターン文字列が一致しない前の位置を指します。j は、前の位置の最長プレフィックス + 1 を参照します。

if(j==0 || ch[i] == ch[j]){
    next[++i] = ++j;
}else{
    j = next[j];
}

このコードは、次の配列を見つけるための核となるコードです. 判定条件 j == 0 は、パターン文字列の前の位置の接頭辞が -1 (文字列の先頭) であることを意味し、 ch[i] == ch[ j] は、パターン文字列の現在の位置がパターン文字列と同じであることを意味します。図に示すように、前の位置のプレフィックスの最後の桁が一致します。

 このとき、i は現在位置のパターン文字列が一致しない、つまり c が一致しない前の位置を指し、i は b を指します。j は、前の位置の最長のプレフィックス + 1 を参照します。つまり、abaa のプレフィックスは 1 で、j は 2 を指します。したがって、位置文字列 abaab に対応する c のプレフィックスは、前の 1 に 1 を加えたものにする必要があります。

故にnext[++i] == ++j。

上記は現在位置の文字が一致する場合なので、現在位置が一致しないとどうなるか。このとき、b と a は一致しません。

 ただし、j には、最初の 4 文字で構成されるプレフィックスの長さが格納されます。長さを 1 と仮定します。

 赤の接頭辞と接尾辞は同じで、青の接頭辞と接尾辞も同じであるため、赤の接尾辞部分も青の構造を持つ必要があると推定されます。

 したがって、文字が一致しない場合は、j = next[j] を設定して小さいプレフィックス (青) を取得し、実際には比較である ch[j] == ch[i] を比較します。

 i と j が一致する場合は、next[++i] = ++j; 一致しない場合は、文字列の先頭が見つかるまで、つまり j == 0 になるまで、より小さいプレフィックスの検索を続けます。

次配列解法動画について次配列コードを見つけるKMPアルゴリズムの説明_哔哩哔哩_bilibili

2.2 まとめ

つまり、次の配列を見つけるには 2 つのケースがあります。

現在のiが指す文字は、前の位置のプレフィックスの後の文字と一致し、現在の位置の最大プレフィックスは前の位置の最大プレフィックス + 1 に等しくなり、i と j はそれぞれ 1 ビット後方にシフトされます。  .

現在のiが指す文字は、前の位置の接頭辞の最後の文字とうまく一致せず、前方に小さい方の接頭辞が見つかりました.前方と後方の接頭辞は同じであるため、小さい方の接頭辞が格納されます次の[j]で。

おすすめ

転載: blog.csdn.net/qq_55796594/article/details/125043413