KMP アルゴリズムの理解 (更新)

KMP アルゴリズムは、非常に古典的な文字列マッチング アルゴリズムです。長さがそれぞれ N と M の 2 つの文字列 str1 と str2 が与えられ、アルゴリズムを実装することについて説明しています。文字列 str1 に str2 が含まれている場合は、開始値を返します。 str1 内の str2 の位置が含まれていない場合は、-1 を返します。タイトルの意味は、下図のように、 str1 と str2 が abcdabce と abce の場合、両者が一致し、str1 の abced の開始位置の添え字 3 を返します。 abcf、この 2 つは異なります。包含関係がある場合は、-1 を返します。この話題を見たとき、多くの人が最初に思いつくのは暴力比較ではないでしょうか str1 の先頭文字位置から str2 を比較し、一致する場合は返します 一致しない場合は 2 番目の文字位置から開始します最初の 3 つ...マッチングまたはマッチングが失敗するまでは、この方法が実際に要件を満たしていることは否定できませんが、消費される時間計算量は O(M*N) であるため、時間計算量を削減する方法はありますか? KMP アルゴリズムがステージにいます。紹介を始める前に、文字列 str の nextArr 配列の概念を理解しましょう。この配列の特徴は何ですか: 1. この配列の長さは、str 文字列の長さと同じです。2 . nextArr [i] の意味は、文字列 str[0...i-1] 内の str[i] より前の str[i-1] で終わる必要があるサフィックス部分文字列です (str[0] を含めることはできません)。 end with str[ 0] プレフィックス部分文字列の一致する最大長 (str[i-1] を含めることはできません)。これは簡単な例です。誰もが理解できると思います。文字列 str が abcdabcd であると仮定します。次に、その nextArr 配列を取得する方法を示します。まず結果をリストします。結果は次のようになります: [-1,0,0 ,0,0,1, 2、3]。1. まず、i=0 の場合、str[0] の前に文字列がなく、このときのデフォルト値は -1 です。 2. 次に、i=0 の場合2. str[1] の前には文字列 a が 1 つだけあり、この時点ではデフォルトで 0 です。 3. 次の i=2、str[2] の前の文字列は ab です。この時点では、a と b は等しくありません。 nextArr[2]=0 4 . Next i=3、str[3] の前の文字列は abc、nextArr[3]=0 5. 同様に、nextArr[4]=0 6. i=5 まで、str[5] が abcda になる前、この時点では、プレフィックス部分文字列とサフィックス部分文字列の間で一致する文字である文字 a が存在します。nextArr[5]=1 7. i=6 のとき、Before str[5] は abcdab で、今度は文字列 ab があります。これは、接頭辞の部分文字列と接尾辞の部分文字列の間の最長一致である nextArr[6]=2 8. 同様に、nextArr[7]=3 が得られるときを見てみましょう。 nextArr 配列、時間計算量はどのように最適化されますか。元の質問に戻りますが、解決しなければならない問題は、文字列 str1 に str2 が含まれているかどうかを判断することです。 str1 と str2 が左側の部分で完全に一致すると仮定した場合、最長一致文字列、つまり a と b は等しいです。ただし、 str2[j] の位置でマッチングが失敗します。つまり、 str2[j] が str1[i] と等しくありません。この時点では、str2 を右にスライドさせるアプローチではなくなっていることに注意してください。ただし、j-nextarr[j] (一致した長さ - プレフィックスとサフィックスの最大共通長) 単位だけ右にスライドして続行します。 上記のプロセスは一致を開始し、時間の最適化が完了します。これは、KMP アルゴリズムの中核ステップでもあります。ここで、文字列の nextArr 配列を取得するメソッドがあるとします。このメソッドの名前が getNextArr であると仮定します。KMP アルゴリズムのコードを以下に示します。 public int getIndex (String str1, String str2){ 同様に、 nextArr[7]=3 nextArr 配列を取得した後、時間計算量がどのように最適化されるかを見てみましょう。元の質問に戻りますが、解決しなければならない問題は、文字列 str1 に str2 が含まれているかどうかを判断することです。 str1 と str2 が左側の部分で完全に一致すると仮定した場合、最長一致文字列、つまり a と b は等しいです。ただし、 str2[j] の位置でマッチングが失敗します。つまり、 str2[j] が str1[i] と等しくありません。この時点では、str2 を右にスライドさせるアプローチではなくなっていることに注意してください。 1 単位だけですが、j-nextarr[j] (一致した長さ - プレフィックスとサフィックスの最大共通長) 単位だけ右にスライドして続行します。 上記のプロセスにより一致が開始され、時間の最適化が完了します。これは、KMP アルゴリズムの中核ステップでもあります。ここで、文字列の nextArr 配列を取得するメソッドがあるとします。このメソッドの名前が getNextArr であると仮定します。KMP アルゴリズムのコードを以下に示します。 public int getIndex (String str1, String str2){ 同様に、 nextArr[7]=3 nextArr 配列を取得した後、時間計算量がどのように最適化されるかを見てみましょう。元の質問に戻りますが、解決しなければならない問題は、文字列 str1 に str2 が含まれているかどうかを判断することです。 str1 と str2 が左側の部分で完全に一致すると仮定した場合、最長一致文字列、つまり a と b は等しいです。ただし、 str2[j] の位置でマッチングが失敗します。つまり、 str2[j] が str1[i] と等しくありません。この時点では、str2 を右にスライドさせるアプローチではなくなっていることに注意してください。ただし、j-nextarr[j] (一致した長さ - プレフィックスとサフィックスの最大共通長) 単位だけ右にスライドして続行します。 上記のプロセスは一致を開始し、時間の最適化が完了します。これは、KMP アルゴリズムの中核ステップでもあります。ここで、文字列の nextArr 配列を取得するメソッドがあるとします。このメソッドの名前が getNextArr であると仮定します。KMP アルゴリズムのコードを以下に示します。 public int getIndex (String str1, String str2){
//両方の文字列が空である場合、または一致する文字列の長さが一致する文字列の長さより長い場合は、直接 -1 を返します
if (str1 == null || str2 == null || str2.length > str1.length ) { return -1; } char[] ch1 = str1.toCharArray(); char[] ch2 = str2.toCharArray(); //j が str2 の最後の文字に達したとき、i、j はそれぞれ str1 と str2 のポインタを表します、試合は成功です。int i = 0; int j = 0; int[] nextArr = getNextArray(ch2); while (i < ch1.length && j < ch2.length) { //一致した場合は次の位置を比較if (ch1 [i ] == ch2[j]) { i++; j++; } //nextArr[j]==-1 の場合、一致する文字列インデックスが 0 であることを意味し、ここでのデフォルト値は -1 のみです; else if (nextArr[ j] == -1) { i++; } //それ以外の場合、一致する文字列は右にスライドします。ここで、 j = nextArr[j] は右にスライドするものとして理解できます。 else { j = nextArr[j]; }






















j
== ch2.length を返す? i - j : -1;

	}接下来就介绍一下如何获取nextArr数组前面就介绍过按照规定字符串第一个字符对应的数组值为-1,第二个为0,即nextArr[0] = -1;nextArr[1] = 0;对于后面的求解过程,下面详细介绍:因为是从左到右依次求解,所以当求解nextArr[i]的时候,nextArr[i-1]已经求解出来,通过它的值可以知道B字符前字符串的最长前缀与最长后缀的匹配区域,a区域与b区域,字符C与字符B分别是紧贴着这两个区域后面的字符,由此可知,如果C字符与B字符相同,那么nextArr[i]=nextArr[i-1]+1。如果字符C与字符B不等,那么就看字符C之前的前缀与后缀的匹配情况了,假设字符C是第cn个字符,那么nextArr[cn]就是其最长前缀与后缀匹配的长度,如下图所示,那么,n与m两个就是最长前缀与后缀区域,m'是b区域的最右区域且长度与m区域长度一致,那么m与m'一定是相等的,字符D是n区域后面一个元素,如果D字符与B字符相等,那么nextArr[i]=nextArr[cn]+1。如果不等那么继续往前跳到字符D,之后的过程与跳到C一致,每跳一次都会出现一个字符与B比较,如果相等,nextArr[i]就可以确定。如果跳到最左的位置,此时nextArr[0]=-1,此时说明字符A之前的字符串不存在前缀后缀匹配,令nextArr[i]=0;具体代码如下:public int[] getNextArray (String s){
		char[] ch = s.toCharArray();
		if (ch.length == 1) {
			return new int[]{-1};
		}
		int[] nextArr = new int[ch.length];
		nextArr[0] = -1;
		nextArr[1] = 0;
		int pos = 2;
		int cn = 0;
		while (pos < next.length) {
		//如果字符B等于字符C,加一
			if (ch[pos - 1] == ch[cn]) {
				next[pos++] = ++cn;
			} else if (cn > 0) {
				cn = next[cn];
			} else {
				next[pos++] = 0;
			}
		}

		return next;

	}

おすすめ

転載: blog.csdn.net/weixin_44313315/article/details/106875841