1. KMP算法是啥?
KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位神人共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 算法。该算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯
,从而使算法效率有了某种程度的提高。
2.KMP算法代码
public class KMP算法 {
public static void main(String[] args) {
String str1 = "BBCABCDsasna:commit:asasa:commit";
String str2 = "commit";
System.out.println(str1.indexOf(str2));
int[] next=kmpNext(str2);
System.out.println("Arrays.toString(next) = " + Arrays.toString(next));
int index = kmpSearch(str1, str2, next);
System.out.println("index = " + index);
}
/**Kmp搜索算法
* @param str1 源字符串
* @param str2 字串
* @param next next 字串对应的部分匹配表 如果没有就返回-1,否则返回第一个匹配的位置
* @return
*/
private static int kmpSearch(String str1, String str2, int[] next) {
//遍历
for (int i = 0,j=0; i < str1.length(); i++) {
//需要处理 str1.charAt(i)!=str2.charAt(j),去调整j的大小
//kmp算法核心
while (j>0&&str1.charAt(i)!=str2.charAt(j)){
j = next[j - 1];
}
if (str1.charAt(i)==str2.charAt(j)){
j++;
}
if (j==str2.length()){
return i - j + 1;
}
}
return -1;
}
private static int[] kmpNext(String dest) {
//创建一个next数组,数组保存部分匹配值就是0
int[] next = new int[dest.length()];
//字符串长度为1,部分匹配值就是0
next[0] = 0;
for (int i = 1,j=0; i < dest.length(); i++) {
//当dest.charAt[i]!=dest.charAt[j],我们需要从next[j-1]获取新的j
//直到我们发现有dest.charAt[i]==dest.charAt[j]成立才退出
//这才是Kmp算法的核心
while (j>0&&dest.charAt(i)!=dest.charAt(j)){
j = next[j - 1];
}
//当dest.charAt[i]==dest.char[j]满足时,部分匹配值就是+1
if (dest.charAt(i)==dest.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}
}
3.为啥String类的indexof不使用KMP?
JDK的String类中的indexof方法的实现,仅仅只是用了暴力破解法,也就是最原始的实现,时间复杂度也到了O(n*m)。
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
谷歌并翻了下StackOverflow:
原来JDK的编写者们认为大多数情况下,字符串都不长,使用原始实现可能代价更低。因为KMP和Boyer-Moore算法都需要预先计算处理来获得辅助数组,需要一定的时间和空间,这可能在短字符串查找中相比较原始实现耗费更大的代价。而且一般大字符串查找时,程序员们也会使用其它特定的数据结构,查找起来更简单。这有点类似于排除特定情况下的快速排序了。不同环境选择不同算法。
Reference:
http://stackoverflow.com/questions/19543547/why-jdks-string-indexof-does-not-use-kmp/