嚴蔚敏書筆記之KMP算法

背景:在長串A中查找子串B 

變量:i———->匹配的過程中,指向父串A待匹配的字符,是它的下標

j———>匹配的過程中,指向子串B待匹配的字符,也是下標

KMP算法介紹: 

1.普通字符串匹配算法:這裏介紹兩種思路

先是和KMP對應的思路,也是嚴蔚敏書上講的思路——從左往右遍歷父串A,當A[i++]==B[j++]並完全匹配B時,i只要繼續向後移一位(++i)即可,j重新置0;當A[i++]和B[j++]在某個字符處失配時,要使i=i-j+1(即將i恢復成開始匹配的狀態並向後移動一位),j也要置0======>>此處也就產生了KMP算法要解決的問題:i的回溯

然後是我的想法(其實基本上完全一樣,寫在這裏是因爲比較一下讓自己印象更清楚)——大體同上,不同的是,匹配過程中i不向前移動,而是利用j的值比較A[i+j]和B[j++],完全匹配時,i=i+j(此時j已經爲子串B的長度);不匹配時只要使i++及j=0,這樣也就掩蓋了“i的回溯”這一問題:(

2.KMP算法講解:

對於父串和子串中有較多的重複字符來說,i的回溯將成爲比較嚴重的問題(特別是在子串較長的情況下),解決方法的思路在於:

如果在失配的時候父串已經和子串匹配了一部分(假設父串爲[0…20],已匹配的部分是[3…8],即開始匹配的字符下標是3,已經匹配了6個字符),那麼有可能[4…8]、[5…8]、[6…8]、[7…8]、[8]是匹配的(也可能那幾個都不匹配);不可能[4…6]是匹配的而[8]是不匹配的,這樣的父串並沒有匹配,因此沒必要繼續匹配。

所以i就不用回溯了,只要讓j爲它應該處在的位置即可,比如上面那幾個區間j分別對應5,4,3,2,1,0;如果兩串比較第一個字符的時候就失配,此時j不能爲子串的任何一個下標,不如讓它等於-1;一旦出現j==-1,需要++i,j=0——或者,更好爲++i,++j

==>>也就是說,失配的時候i不變,j要進行漂移,也就是要獲得j下一個值,我們用next數組存儲。next[j]表示下標爲j在失配時應該漂爲的下標

先給匹配函數:

void KMP(char *A, char *B, int *next, int alen, int blen){
	int i=0, j=0;
	while(i<alen){
		if(j==0 || A[i]==B[j]){
			++i, ++j;
		}
		else{
			j=next[j];
		}
	} 
}

再給next數組獲取函數:

void getNext(char *B, int *next, int blen){
	int i=0, j=-1;//j=-1說明第一個字符就失配
	next[0]=-1;
	while(i<blen){
		if(j==-1 || B[i]==B[j]){
			++i, ++j;
			next[i]=j;//這也是個關鍵點,表示[0...(j-1)]的字符與[(i-j-1)...(i-1)]是匹配的,當第i個字符失配時,將對父串[i]和子串[j]進行比較
		}
		else{
			j=next[j];
		}
	}
}

對於證明還是不能很好理解,以後理解了再補吧。

点赞