KMP算法分析
1、问题出现
给出一个字符串为source,和另一个字符串target。
如果这个target是source的字串,返回这个子串的开始的索引。如果不是子串则返回-1。
2、解决问题的思路
任何一个算法都说从暴力法优化得到的,KMP算法也不例外。这次我们列出暴力法如下:
1 public class KMP { 2 public static int match(String source,String target) { 3 int i=0;//原字符串的下标 4 int j=0;//目标子串的下标 5 int k=-1;//记录第几个字符 6 while(i<source.length()) { 7 while(j<target.length()&&source.charAt(i)==target.charAt(j)) { 8 i++; 9 j++; 10 }//子串和目标串进行匹配 11 k++;//首字符位置每次循环后+1。 12 if(j==target.length()) { 13 return k; 14 }else { 15 j=0; 16 i=k+1; 17 } 18 19 } 20 return -1; 21 } 22 public static void main(String[] args) { 23 System.out.println(match("abababca","bab")); 24 } 25 }
这个算法有俩层循环嵌套显得十分臃肿。这时候就需要对算法进行优化。
3、KMP算法的引入
上面的算法每次匹配失败之后,我们会都移动i和j。
而KMP则只需要我们每次只移动需要匹配的字串的j 不移动原字符串的i。这样就少了一层循环。
那j需要移动到哪呢?KMP为原字符串准备了个next数组。
先不用管如何实现的。我们先试试这个方法。
public class KMP2 { static int[] next = {-1,0,0,1,2,3,4,0}; public static int match(String source,String target){ int i=0; int j=0; while(i<source.length()) { if(j == -1 ||source.charAt(i)==target.charAt(j)) { i++; j++; } else { j=next[j];//这里我们只给j赋值 } if(j==target.length()) { return i-j;//返回匹配开头的字符 } } return -1; } public static void main(String[] args) { System.out.println(match("abababca","bab")); } }
结果是一样的。这就少了一层循环。
4、NEXT数组如何求的呢?
总共分4步骤
1、找字符串的前缀式:例如 aba这个 前缀式就是a、ab
2、找字符串的后缀式:例如 aba这个字符串的后缀式为ba、a
3、找前后俩个字符串相同的最长字串、的长度,这里的aba中a相同,因此就是1
但是如果未找出就得0;
如上所示。
4、把我们之前求的数,向右移。一位就是next数组了。
如果之前没做过的,一定要手动算一下。算完了就记住了。
5、算法求出NEXT数组。
算法实现是个难理解的地方。
记住俩概念:索引值为0和1的俩个数设为-1和0,不为什么。记住
public class KMP2 { //static int[] next = {-1,0,0,1,2,3,4,0}; public static int[] getNext(String source) { int[] next = new int[source.length()]; // 初始条件 int j = 0; int k = -1; next[0] = -1; // 根据已知的前j位推测第j+1位 while (j <source.length() - 1){ if (k == -1 || source.charAt(j) ==source.charAt(k)){ next[++j] = ++k; } else{ k = next[k]; } } return next; } public static int match(String source,String target){ int i=0; int j=0; int[] next = getNext(source); while(i<source.length()) { if(j == -1||source.charAt(i)==target.charAt(j)) { i++; j++; } else { j=next[j]; } if(j==target.length()) { return i-j; } } return -1; } public static void main(String[] args) { System.out.println(match("abababca","bab")); } }
微信搜索桔子科研或者扫描二维码,第一时间获取编程有趣的知识和最新科研学术成果。