KMP算法
KMP算法
1.KMP算法的应用场景:字符串匹配问题。
假设str1 = BBC ABCBAB ABCDABCDABDE, str2 = ABCDABD,然后判断str1是否还有str2,如果存在,就返回第一次出现的位置 ,如果没有返回-1。
解法1:暴力匹配算法
假设str1匹配到i位置,字串str2匹配到j位置,则
1)如果当前匹配成功(即str[i] = str[j]),则i++, j++,继续匹配下一个字符。
2)如果匹配不成功(即str[i]!=str[j]),令i = i-j+1(i回到开始匹配的字符的后一个位置), j = 0(回到初始位置),相当于每次匹配失败,i回溯,j重置。
由此可以看出暴力匹配方法会有大量回溯,浪费大量时间,不可行。
2.KMP算法介绍
1)KMP算法是一个解决模式串在文本串是否出现过,如果出现过,返回最早出现的位置的经典算法。
2)部分匹配表产生
字符串:“bread”; 前缀: b, br, bre, bred 后缀:read, ead, ad, d
部分匹配值就是前缀和后缀的最长的共有元素的长度,以“ABCDABD”为例:
“A”的前缀和后缀都为空集,所以共有元素的长度为0。
“AB”的前缀为[A],后缀为[B],共有长度为0。
“ABC”的前缀[A,AB], 后缀为[BC,C],共有长度为0。
"ABCD"的前缀[A,AB,ABC],后缀为[BCD,CD,D],共有长度为0。
“ABCDA”的前缀[A,AB,ABC,ADCD],后缀为[BCDA,CDA,DA,A],共有元素为“A”,所以共有长度为1;
“ABCDAB”的前缀[A,AB,ABC,ABCD,ABCDA],后缀[BCDAB,CDAB,DAB,AB,B],共有元素为“AB”,所以共有长度为2;
“ABCDABD”的前缀[A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀[BCDABD,CDABD,DABD,ABD,BD,D],共有长度为0;
由上可得出部分匹配表为
搜索词 | A | B | C | D | A | B | D |
---|---|---|---|---|---|---|---|
部分匹配值 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
解法2 :
package com.ll.kmp;
public class KMPAlgorithm {
public static void main(String[] args) {
String str1 = "BBC ABCDAB ABCDABCDABDE";
String str2 = "ABCDABD";
int[] next = kmpNext(str2);
System.out.print(kmpSearch(str1, str2, next));
}
/**
* kmp匹配算法
*
* @param str1 源字符串
* @param str2 目标字符串
* @param next 部分匹配表
* @return 返回对应位置,未找到返回-1
*/
public 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的大小
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;
}
/**
* 获取字符串的部分匹配表
*
* @param dest 目标字符串
* @return
*/
public static int[] kmpNext(String dest) {
// 创建部分匹配表
int[] next = new int[dest.length()];
next[0] = 0; // 如果字符串长度位1, 部分匹配值为0;
for (int i = 1, j = 0; i < dest.length(); i++) {
while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
j = next[j - 1];
}
// 如dest.charAt(i) == dest.charAt(j),则部分匹配值j++
if (dest.charAt(i) == dest.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
}