KMP算法 左神 最傳統 最詳細的思路 JAVA

本文只是一個學習後的總結,可能會有錯誤,歡迎各位指出。任意轉載。算法

題目:給定一個字符串 str1 和一個字符串 str2,在字符串 str1 中找出字符串 str2 出現的第一個位置 (從0開始)。若是不存在,則返回 -1。數組

str1 = aaaaabcabc學習

str2 = abcabcaa優化

前段時間偶然接觸到左神的算法講解視頻,大概三天的時間,反反覆覆把 KMP 算法看了三遍。終於有了一些本身的理解與體會。用傳統的 KMP 算法去作字符串匹配,實際上是用 next 數組對暴力算法的一個優化。另一種理解是將 KMP 算法理解爲動態規劃,這裏不詳細敘述。編碼

這裏我分爲三部分來說。spa

  1. 暴力解法
  2. KMP 算法
  3. 如何求 next 數組

暴力解法

暴力算法看起來很是簡單,實際編碼仍是須要處理一些細節的,建議寫一寫。這裏的給 str1 一個 i 指針,給 str2 一個 j 指針。i 的第一個初始位置是 0,最後一個初始位置是 str1.length - 1。指針

  1. str1[ i ] 和 str2[ j ]相等: i 和 j 都日後移動一位。code

  2. str1[ i ] 和 str2[ j ]不等,j 歸 0, i 從下一個初始位置開始比較。視頻

若是 j 可以到 length 這個位置上,說明從第 0 位到第 str2.length - 1 位都已經相等了,此時返回 i - j ,就是 str2 在 str1 中出現的第一個位置的 index 。字符串

若是 i 到達了最後一個初始位置,也就是 str1.length - 1 ,此時尚未匹配成功,那麼說明永遠都沒辦法匹配到 str2 。 這個時候返回 -1 。

代碼:

public int strStr(String str1, String str2) {
	int length1 = str1.length();
	int length2 = str2.length();
	if(length2 == 0) return 0;
	if(length1 < length2) return -1;
	int i = 0;
	while(i < length1){
		int j = 0;
		while(i < length1 && j < length2
		&& str1.charAt(i) == str2.charAt(j)){
			i++;
			j++;
		}
		if(j == length2){
			return i-j;
		}
		i = i - j + 1;
	}
	return -1;
}

仍是建議動手寫一下。

KMP算法

這裏暫時先不討論 next 是如何來的。你須要知道它存放的是 str2 的一些信息。他的值等於 str2 前面的全部字符造成的字串的前綴等於後綴的最大值。這裏很是繞,舉個例子來講明:

index 等於 6 的時候, 字串是 a b c a b c

先後綴取 1 的時候,前綴爲 a, 後綴爲 c,此時不等。 next 不能取 1 。

先後綴取 2 的時候,前綴爲 ab, 後綴爲 bc, next 不能取 2 。

先後綴取 3 的時候,前綴爲 abc, 後綴爲 abc, 此時相等, next 能夠取 。

先後綴取 4 的時候,前綴爲 abca, 後綴爲 cabc, next 不能夠取 4 。

先後綴取 5 的時候,前綴爲 abcab, 後綴爲 bcabc, next 不能夠取 5 。

先後綴不能夠取 6 。由於先後綴不能夠爲字符串自己。

index:0 1 2 3 4 5 6 7 8 9

str1 = a a a a a b c a b c

str2 = a b c a b c a a

next:-1 0 0 0 1 2 3 1

接下來是 KMP 算法的流程。按照暴力的解法,咱們仍是有兩根指針 i 和 j。

  1. 兩個元素相等時: i 和 j 日後移動一位。
  2. 兩個元素不等時: j = next [ j ],若是此時 next[ j ] 等於 -1,說明 j 指針已經移動到了最前面。

咱們來仔細理解這個不相等的兩種狀況,這裏是難點。

next[j] != -1,這種狀況下,j 指針直接跳到 str2[next[j]]去。爲何這樣作能夠?舉例子。

index 0 1 2 3 4 5 6 7

str1 = a b c f a b c x

str2 = a b c f a b c y

next=-1 0 0 0 0 1 2 3

在 index 爲 6的時候,i = j = 7,這個時候兩個元素不相等,咱們會把 j 跳到 str2[next[j]],也就是 j = 3。這個時候 str1 前面的子串 和 str2 前面的子串是相等的,他們擁有共同的 next 數組。j 跳到 3,這個 3 表明: y/x 前面這個子串他的前三位和後三位相等。那麼,咱們的 y 的子串前三位 和 x 子串的後三位這個時候是否是就不須要比較了,由於這個 3 默認了他們相等。那麼前三位(index爲 0 1 2)就不須要比較了,直接比較第四(index 爲 3 )位。這裏就是 next 數組的核心。在左神的視頻裏面講得更直觀。

str1 = a b c f a b c x

str2 = * * * * a b c f a b c y

比較 x 與 f 是否相等。

next[j] == -1,這種狀況下,j 已經來到最前面了,沒辦法繼續前移,那麼只能 i 向後移。

代碼:

public static int getIndexOf(char str1[], char str2[]) {
		if(str1.length == 0 || str1.length < str2.length) {
			return -1;
		}
		if(str2.length == 0) {
			return 0;
		}
		int i = 0;
		int j = 0;
		int next[] = getNextArray(str2);
 	//對應三種狀況
		while( i < str1.length && j < str2.length) {
			if(str1[i] == str2[j]) {
				i++; //兩個元素相等
				j++;
			}else if(next[j] == -1) {
				i++; //next[j] == -1
			}else {
				j = next[j];//next[j] != -1
			}
	} 
	return (j == str2.length) ? i-j : -1;
	}

next數組

str2 = a b c f a b c y

next=-1 0 * * * * * *

第一位默認爲 -1。 由於第一位元素沒有子串。

第二位默認爲 0。由於第二位元素的子串只有一個元素,那他的先後綴最大相等數目只能爲0。

接下來是第三位,第三位的子串是a b,這裏是難點。如何求出它的 next 值。j = 3

用 j - 1 的 next 的值,cn = next[j-1]的 str2 對應的元素, 和 str2[j-1] 比較。這裏的cn = 0, 那比較的就是第 0 號元素和第 1 號元素的值。比較出來必定有兩種狀況,相等,不相等。而在不相等的時候又要分兩種狀況。

index 0 1 2 3 4 5 6 7

str2 = a b c f a b c y

next=-1 0 0 0 0 1 * *

爲了更直觀看見,我換個例子。j = 6.

cn = next[j-1] = 1, str2[cn] = b

str2[j-1] = b

這個時候是相等的,所以 next[6] = ++cn = 2 。爲何?

這個 cn 表明的是什麼?cn 表明的是 j-1 位的 next 值,這個值表明 j-1 位的先後綴最大值。這個最大值是 1,說明他第一位和最後一位相等。那麼比較他的第二位(str2[cn])和最後一位的下一位(str2[j-1])是否相等。相等的話,next[6] = ++cn = 2 。不相等怎麼辦?分爲兩種狀況。

  1. cn > 0,cn = next[cn]
  2. cn<= 0,next[j] = 0

這裏又是爲何,就是在子串的狀況下繼續分,去找到和str[j-1]相等的 cn,若是一直找不到呢?怎麼辦,那next[j] = 0

代碼:

public static int[] getNextArray(char []str) {
	if(str.length == 1) {
		return new int [] {-1};
	}
	int next[] = new int [str.length];
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int cn = 0;
	while( i < str.length) {
		if(str[i-1] == str[cn]) {
			next[i++] = ++cn;
		}else if(cn > 0) {
			cn = next[cn];
		}else {
			next[i++] = 0;
		}
	}
	return next;
}

小結一下

  1. 暴力解法,多去寫,多寫兩遍就熟練。
  2. KMP 具體實現,有三種狀況。元素相等、元素不等且 next 不等於-一、元素不等且 next 等於 -1。
  3. next 的求解方法,也是三種狀況。cn 和 j-1 對應的元素相等、 對應的元素不等且cn>0、 對應的元素不等且cn<=0。

公衆號 stul 同步更新算法學習過程,歡迎關注。

相關文章
相關標籤/搜索