KMP算法实践与简单分析
Posted Qcer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 package agstring; 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算法实践与简单分析的主要内容,如果未能解决你的问题,请参考以下文章
编程实践使用golang 解析json字符串代码 / 使用 golang 实现一个HashSet / 使用C语言实现KMP算法,并加上非常详尽的注释。