概述:本文主要在代碼層面上分析KMP的實現(xiàn)過程,如果您還不了解KMP的推導過程,請參考
KMP(一) 模式匹配算法推導 --《部分匹配表》
KMP(二) 模式匹配算法實現(xiàn)
KMP(三) 字符串快速匹配示例
字符串快速匹配Demo下載
一.next 數(shù)組計算:
KMP(一) 模式匹配算法推導 --《部分匹配表》中給出的部分匹配值計算方式,用代碼貌似并不容易實現(xiàn)(孤陋寡聞了,目前我還沒想出來好的計算方式);我們再來看一種利用next 數(shù)組值實現(xiàn)KMP的方式;首先我們要計算next數(shù)組;
同樣針對長度為 L 的字符串 T 先給出一個約定:
前綴: 字符串T[0~K], 0 <= k <=(L -1);
后綴:字符串T[K~L],0 <= k <=(L -1)
注意:此處沒有組合的概念,表達不同于KMP(一) 模式匹配算法推導 --《部分匹配表》中的前綴和后綴
在給出next計算方式之前,結合KMP 算法推導中部分匹配值的計算方式,先看示例:
S = "ABAABAA"; 對應下表記為 i, 計算其next 數(shù)組;
- i=0, 字符A前面沒有字符串,默認約定next[0] = -1;
- i = 1,字符B前面字符串"A",前綴和后綴都不存在,默認約定 next[1] = 0;
- i = 2,字符A前面字符串"AB", 前綴后綴不純在相等,則next[2]=0;
- i = 3,字符A前面字符串"ABA",前綴和后綴相等部分為A,則next[3]=1;
- i = 4,字符B前面字符串"ABAA",前綴和后綴相等部分為A,則next[4]=1;
- i = 5,字符A前面字符串"ABAAB",前綴和后綴相等部分為AB,則next[4]=2;
- i = 6,字符A前面字符串"ABAABA",前綴和后綴相等部分為ABA,則next[4]=3;
即: next[L] = [-1,0,0,1,1,2,3];
所謂的next數(shù)組就是求字符S[i]前面字符串中前后綴相等的最大長度而已。
二.next數(shù)組的作用:
與部分匹配值作用一致,next數(shù)組就是保存著當主串和模式串不匹配時,接下來與主串P[j]字符比較的模式串S[i]的位置,即S[i']=S[next[i]]。如:
主串: P = "ABABAABBB"; 下標以 j 表示
模式串(字串): S = "ABAABAA"; 下標以 i 表示
P[0~2] = S[0~2], P[3] != S[3],則比較到下標為 3 的位置時,按照KMP的原理,此時應將模式串 S 的待比較位置 移至 i = next[3] ,即S[next[3]]位置;
抽象圖表示如下:
看到此處,基本已經了解next 數(shù)組的作用,以及KMP結合next的基本實現(xiàn)原理;下面看code;
三. next數(shù)組的代碼實現(xiàn)
前面有對next求解的過程,然而那只是為了理解next的含義,真正算法編程卻無法那樣直觀求出,那該如何求解next數(shù)組呢?這里用到了類似并查集的算法。
主串:abababbbab
- 首先next[0]=-1,next[1]=0;
- 之后每一位j的next求解:
- 比較j-1字符與next[j-1]是否相等,
- 如果相等則next[j]=next[j-1]+1,
- 如果不相等,則比較j-1字符與next[next[j-1]]是否相等,
- 如果相等則next[next[j-1]]+1,
- 如果不相等則繼續(xù)以此下去,直到next[…]=-1,則next[j]=0.
通俗易懂的話來說就是你要求解當前位的next值,則看前一位與前一位next所在位字符是否相等,相等則當前位的next等于前一位next+1,如果不等就找前一位next的next與前一位比較,如果相等,當前位的next等于那個與前一位字符相等的字符所在位置+1,如果還是不相等,則以此類推,直到出現(xiàn)比較位為next[]=-1,那當前位的next就等于-1。
然而在算法求解的時候,我們應該這樣去理解,求解下一位的next等于當前位與當前位的next比較。算法如下:
-(NSArray<NSNumber *> *)getNextWithString:(NSString *)string{
NSMutableArray<NSNumber *> * next = [NSMutableArray arrayWithCapacity:string.length];
next[0] = @(-1);//初始化
int j= 0,k= -1; //j:記錄當前下標; k記錄當前位的next
while (j < string.length - 1) {
if (k== -1 || [string characterAtIndex:j] == [string characterAtIndex:k]) { // 比較當前(j)字符與當期位next處字符是否相等
next[++j] = @(++k); // 移動下標,并求解下一位的next;
}else{
k = [next[k] intValue]; // 回溯當前位的next
}
}
return next;
}
四.KMP 算法的實現(xiàn)
根據上述next數(shù)組的用法,編寫KMP實現(xiàn)代碼如下:
-(NSArray<NSString *> *)indexKMPwithParentString:(NSString *)pString andSubstring:(NSString *)sString{
NSArray *next = [self getNextWithString:sString]; // 計算next數(shù)組
int index_s = 0 ,index_p = 0; //標記 pString 和 sString 的當前比對位置
NSMutableArray<NSString *> *ranges = [NSMutableArray array]; //用于保存匹配結果的range位置
while ((index_p < pString.length) && (index_s < sString.length)) { // 一直比對至兩個字符串結尾
if ([pString characterAtIndex:index_p] == [sString characterAtIndex:index_s]) { // 當前比對位置的字符相等,則移動P和S繼續(xù)下一位比對
if ((index_s == sString.length - 1) && (index_p != pString.length -1)) { // 字串S比對至末尾,但是主串P未到末尾,即是說字串匹配成功,但是尚需確定主串的后續(xù)位置是否能匹配,故繼續(xù)比對
index_s = 0; //將字串S當前比對位置移至起始位置
[ranges addObject:NSStringFromRange(NSMakeRange(index_p - sString.length + 1 , sString.length))]; //保存本次匹配的位置結果
continue; // 完成一次匹配,跳出本次循環(huán) index_p 不再移動
}
index_p ++; // 向后移動P的當前位置
index_s ++; // 向后移動S的當前位置
}else{ //當前比對位置的字符不相等,則保持主串P位置不回溯,根據next數(shù)組回溯字串S
if (index_s != 0) { //特別處理 next[0] = "-1"的情況
index_s = [next[index_s] intValue]; // 根據next回溯當前字串S的比對位置
}else{
index_p ++; //如果起始位置不匹配,則直接移動主串P
}
}
}
return ranges;
}
不才看了兩天半,算是基本弄明白這個KMP吧,代碼中注釋我盡量說的白明些,顯得比較啰嗦,記錄一下,歡迎一起探討;