KMP算法實踐與簡單分析
一、理解next數組
1、約定next[0]=-1,
同時可以假想在sub串的最前面有一個通配符“*”,能夠任意匹配。對應實際的代碼t<0時的處理情況。
2、next[j]可以有如下的幾種理解思路:
1)next[j]為sub[j]前面的字符串的前後綴字符串匹配的最大匹配長度
例如sub=“ababap”
next[5]=3,前後追匹配字符串為“aba”
2)在sub[j]位置匹配失敗後,next[j]為為sub串的位置指針j能夠先前回溯到的位置。
3)next[j]為最長前綴匹配串的下一個字符位置(這就是為什麽求next數組時需要有t=next[t]這一步。)
由此不難發現:
next[j]的值越大,在base[i]與sub[j]處匹配失敗時,sub串的位置指針j需要回溯的跨越長度越小。
反之,
next[j]的值越小,在base[i]與sub[j]處匹配失敗時,sub串的位置指針j需要回溯的跨越長度越大。
極端情況下,next[j]為0,sub串的位置指針j直接回溯到sub串起始位置。
二、理解KMP主算法
1、base串的位置指針i在匹配的過程中始終不會向前回溯,這也是KMP算法較蠻力匹配算法高效的原因。
2、當base[i]和sub[j]匹配失敗時,sub串的位置指針j回溯,j變小,等效於將sub串向右移動。
j回溯到next[j]的位置。
三、理解改進的next數組
改進的next數組的取值優化算法:
if (sub.charAt(t) != sub.charAt(j)) { next[j] = t; }else{ next[j] = next[t]; }
考慮對於base主串和sub串如下:
String base = "aaaabcde";
String sub = "aaaaax";
用改進的next數組取值為[-1,-1,-1,-1,-1,4]
當b=base[4] != sub[4]=x時,j=next[j]=-1,直接跳到sub串的哨兵“*”位置,然後進入j<0,進而i++,j++,中間省略了層層回溯的步驟。
其原理相當於簡化了將KMP主算法中的sub位置指針j的跳轉條件t = next[t];的負擔。
因為在KMP主算法中base[i] != sub[j]時,j經過第一次回溯之後,如果出現sub[[next[j]]]=sub[j]的話,不難推斷sub[[next[j]]]=sub[j]!=base[i],那麽這一次回溯是沒有實際效果的,j必將還要向前回溯。。。基於這樣的考慮,直接對next數組做優化處理,避免了主算法中這樣的層層回溯,能夠減少主算法中while循環的次數。
改進的next數組能夠避免sub串的位置指針j層層向前回溯,保證每次j的回溯都是有效的。
四、java實現如下
1 packageagstring; 2 3 public class KMP { 4 public static int[] getNextAry(String sub){ 5 int subLenght = sub.length(); 6 int[] next = new int[subLenght]; 7 int t = next[0] = -1,j = 0; 8 while(j < subLenght-1){ 9 if(t < 0 || sub.charAt(t) == sub.charAt(j)){ 10 t++; 11 j++; 12 next[j] = t;//可優化 13 }else { 14 t = next[t]; 15 } 16 } 17 return next; 18 } 19 public static int[] getNextAryExt(String sub){ 20 int subLenght = sub.length(); 21 int[] next = new int[subLenght]; 22 int t = next[0] = -1,j = 0; 23 while(j < subLenght-1){ 24 if(t < 0 || sub.charAt(t) == sub.charAt(j)){ 25 t++; 26 j++; 27 next[j] = sub.charAt(t) != sub.charAt(j)?t:next[t]; 28 }else { 29 t = next[t]; 30 } 31 } 32 return next; 33 } 34 35 /* 36 *i為主串位置指針,j為sub串位置指針 37 *j<0的情況為sub串的位置指針為0,且sub[0] != base[i] 38 *匹配能夠成功的情況必為j==subLength 39 * */ 40 public static int matchOfKMP(String base,String sub){ 41 int baseLength = base.length(); 42 int subLength = sub.length(); 43 int i = 0,j = 0; 44 int[] next = getNextAryExt(sub); 45 while(i < baseLength && j < subLength){ 46 if(j < 0 || base.charAt(i) == sub.charAt(j)){ 47 i++; 48 j++; 49 }else { 50 j = next[j]; 51 } 52 } 53 int result = j == subLength?i-j:-1; 54 return result; 55 } 56 57 public static void main(String[] args) { 58 try { 59 String base = "ababghababa"; 60 String sub = "ababap";//chinchilla,ababaaaba, 61 int result = matchOfKMP(base, sub); 62 System.out.println(result); 63 } catch (Exception e) { 64 // TODO: handle exception 65 e.printStackTrace(); 66 } 67 } 68 }
KMP算法實踐與簡單分析