具体学习:
请参照,讲解的非常详细:https://blog.csdn.net/v_july_v/article/details/7041827
KMP解决的问题:字符串中查找。如:S(文本串)=‘aaab’,P(模式串)=‘aab’,在S中查找是否包含P。
暴力的解法:遍历S的时候,匹配P,时间复杂度O(n*m),S 长度n,P 长度m。
而KMP方法时间复杂度,O(n+m),与暴力解是质的变化。
首先了解这个概念:
字符前面的前缀与后缀的最长匹配长度(记为MML:max match length):(1)前缀和后缀的长度要相等。(2)前缀和后缀的字符串要相等。(3)前缀和后缀的长度要最长。(4)前缀不能包含最后一个字符,后缀不能包含第一个字符。(5)不包含这个字符本身,是在当前字符的前面字符串中找。
举例:abcabcd。例如d的MML是3。因为‘abc’== ‘abc’。
next[]数组:即,P中每个字符的MML,人为规定next[0] = -1,next[1] = 0。
KMP算法的流程,因为有了next数组。举例如下:
S = aabcdeaaf
P = aabcdeaak, next [] = {-1, 0, 1, 0, 0 ,0 ,0, 1, 2};
KMP的算法流程(引用了大佬的,自己写的讲不清楚):假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置
如果当前字符匹配成功(即[S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
如果当前字符匹配失败(即S[i] != P[j]),再如果 next[j] == -1,则 i++,j不变。否则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了 j - next [j] 位。
我们发现如果某个字符匹配成功,模式串首字符的位置保持不动,仅仅是i++、j++;如果匹配失配,i 不变(即 i 不回溯),模式串会跳过匹配过的next [j]个字符。整个算法最坏的情况是,当模式串首字符位于i - j的位置时才匹配成功,算法结束。
所以,如果文本串的长度为n,模式串的长度为m,那么匹配过程的时间复杂度为O(n),算上计算next的O(m)时间,KMP的整体时间复杂度为O(m + n)。
代码:
package basic_class_02;
public class Code_01_KMP {
public static int getIndexOf(String s, String m) {
if (s == null || m == null || m.length() < 1 || s.length() < m.length()) {
return -1;
}
char[] ss = s.toCharArray();
char[] ms = m.toCharArray();
int si = 0;
int mj = 0;
int[] next = getNextArray(ms);
while (si < ss.length && mj < ms.length) {
if (ss[si] == ms[mj] ) {
si++;
mj++;
} else if(next[mj] == -1){
si++;
} else{
mj = next[mj];
}
}
return mj == ms.length ? si - mj : -1; // ms 匹配到结尾了,说明已经匹配成功过
}
public static int[] getNextArray(char[] ms) { // next数组获取
if (ms.length == 1) {
return new int[] { -1 };
}
int[] next = new int[ms.length];
next[0] = -1;
next[1] = 0;
int pos = 2;
int cn = 0;
while (pos < next.length) {
if (ms[pos - 1] == ms[cn]) {
next[pos++] = ++cn;
} else if (cn > 0) {
cn = next[cn];
} else {
next[pos++] = 0;
}
}
return next;
}
public static void main(String[] args) {
String str = "abcabcababaccc";
String match = "ababa";
System.out.println(getIndexOf(str, match));
}
}
当然肯定有比KMP更加优秀的算法,如BM和Sunday算法。上文链接中,讲解到了,推荐一看,流程比KMP还要简单。