KMP算法的介绍及其Java实现

来源:互联网 发布:c 源码下载 编辑:程序博客网 时间:2024/05/17 03:52


这里有一篇好文章:http://www.cnblogs.com/c-cloud/p/3224788.html


问题:给定一个字符串,寻找在一个长字符串中有没有与给定字符串匹配的子串。


解决这个问题,称之为字符串匹配算法。当前比较经典的一个算法是KMP算法。具体过程如下:


1)给定两个字符串,从第一位开始对比。当第一位不同时,向后遍历




2)当第一位相同时,继续向后遍历





当遍历到后面时,发现不匹配了。此时就需要继续向后遍历,传统的方式是向后跳一位,但是这就造成了前面成功遍历的计算量的浪费。

KMP算法就是在当前情形的时候,寻找下一跳跳转的位数。


KMP算法是:跳转位数=已匹配的字符数-对应的部分匹配值

上面的例子中,已匹配的字符数为3,已匹配的字符串ABC的部分匹配值为0,所以跳转位数就是 3 - 0 = 3 ,继续向后遍历

至于字符串的部分匹配值,先别care,下面会介绍。


3)跳转之后,按照前面说的准则来做匹配







当前匹配完成一次,继续向后面做匹配,此时已匹配字符数为 4, 而已匹配的字符串ABCD的部分匹配值为0,跳转位数为4.




不匹配,继续向下,而此时已经到了字符串尾,这样一次遍历就完成了。



下面来讲字符串的部分匹配值的计算


字符串的部分匹配值就是当前字符串的前缀字符串集 和 后缀字符串集的公共字符串的最长的长度。


举个例子  ABCDAB

前缀集:A  AB  ABC  ABCD  ABCDA

后缀集:B  AB  DAB  CDAB  BCDAB

所以最长公共字符长度为2,所以这个字符串的部分匹配值就是2


再举个例子  ABCDA

前缀集:A  AB  ABC  ABCD 

后缀集:A  DA  CDA  BCDA

最长公共字符串长度为1,所以这个字符串的部分匹配值就是1



最后再来讲代码实现吧,这里用java实现

import java.util.Arrays;/** * KMP算法的Java实现 */public class KMP {private int[] nextArr = null;private String originStr = null;private String moduleStr = null;public KMP(String originStr, String moduleStr) {this.originStr = originStr;this.moduleStr = moduleStr;this.nextArr = caculate_nextArr(moduleStr);}/** * 计算next数组的值(部分匹配值) * */private int[] caculate_nextArr(String str) {if (str == null || str.length() == 0) {return null;}int[] theNextArr = new int[str.length()];for (int i = 0; i < str.length(); i++) {if (i == 0) {theNextArr[i] = 0;} else if (i == 1) {if (str.charAt(0) == str.charAt(1)) {theNextArr[i] = 1;} else {theNextArr[i] = 0;}} else {int theLength2 = i;boolean hasEqual = false;for (int j = theLength2 - 1; j >= 0; j--) {String prefix_str = str.substring(0, j + 1);String suffix_str = str.substring(theLength2 - j,theLength2 + 1);if (prefix_str.equals(suffix_str)) {hasEqual = true;theNextArr[i] = prefix_str.length();break;}}if (hasEqual == false) {theNextArr[i] = 0;}}}return theNextArr;}/** * 获取子字符串在父字符串中的位置 */public int getIndexOfStr() {if (moduleStr == null || moduleStr.length() <= 0) {return -1;}if (originStr == null || originStr.length() <= 0) {return -1;}if (originStr.length() < moduleStr.length()) {return -1;}int res = -1;int totalLength = originStr.length();int origin_loc = 0;int module_loc = 0;while (true) {char c_origin = originStr.charAt(origin_loc);char c_module = moduleStr.charAt(module_loc);if (c_origin == c_module) {if (module_loc == moduleStr.length() - 1) {res = origin_loc - module_loc;break;} else {origin_loc++;module_loc++;}} else {if (module_loc == 0) {origin_loc++;} else {module_loc=nextArr[module_loc-1];  }}if (origin_loc >= totalLength) {break;}}return res;}// 测试public static void main(String[] args) {KMP ktest = new KMP("BBC ABCDAB ABCDABCDABDE", "ABDE");System.out.println("部分匹配值的next数组:");System.out.println(Arrays.toString(ktest.nextArr));int theLoc = ktest.getIndexOfStr();System.out.println();System.out.println("匹配位置在:" + theLoc);}}





0 0