这里有一篇好文章: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);
}
}