KMP字符串匹配算法
KMP 是由三个计算机科学家首字母命名的字符串匹配算法,主要实现思路是
出现第1个不匹配的坏味道字符时,记录模式串匹配的前缀,计算出匹配前缀,需要移动到 “不匹配字符” 的位置;
减少主串和模式串 一一对应 比较的次数。
KMP 算法空间复杂度为: O(n+m); 其中主串长度:n, 模式串长度为:m
实现思路:
主串: G T G T G A G C T G
模式串: G T G T G C F 对应数组下标为 0 1 2 3 4 5 6
第一轮比较,标识第1个不匹配坏味道字符,如下;
主串: G T G T G A G C T G
模式串: G T G T G C F
可以看到模式字符串的匹配前缀为: G T G T G,根据匹配前缀,前3个字符的和后3个字符是一样的,这样就可以进行如下操作:
模式串下标为3的字符 移动到 主串 A(不匹配字符) 的位置
第二轮比较,标识第1个不匹配坏味道字符,如下;
主串: G T G T G A G C T G
模式串: G T G T G C F
可以看到模式串 匹配前缀为: G T G, 根据匹配前缀,可以进行如下操作:
模式串下标为1的字符 移动到 主串 A(不匹配字符) 的位置
第三轮比较,标识第1个不匹配坏味道字符,如下;
主串: G T G T G A G C T G
模式串: G T G T G C F
根据这三轮的比较和移动,我们可以总结出KMP 算法的核心:
当出现第1个不匹配坏字符的时候,根据“匹配前缀” 计算出 移动 “模式串” 的下标为i的字符 到 坏字符的位置,
并且保证 “模式串” 中小于下标i的字符串和主串是匹配的。
从中我们可以指定:移动“模式串”下标i到 主串 坏字符位置,只和 “匹配前缀”有关;“匹配前缀” 只和 “模式串” 有关
这样就可以引入一个 next 数组,来记录 “匹配前缀”的移动“模式串”的下标位置,next 数组是kmp算法的灵魂,理解了next数组的
计算逻辑,就能完全理解 kmp 算法;以下先给出 next 数组:
next 数组:
值(移动模式串下标) | 0 | 0 | 0 | 1 | 2 | 3 | 0 |
下标(模式串下标) | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
字符 | G | T | G | T | G | C | F |
模式串数组:p
值 | G | T | G | T | G | C | F |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
next 数组的计算过程如下:
步骤1 :开始时: i=2,j = 0, i>j; next[0] = 0;next[1]=0
步骤2 :如果 p[i-1] = p[j] 时,next[i] = ++j, 否则到步骤3
步骤3 :当j=0 时 next[i] = 0;j 不为0,则取 j=next[j] 回溯 步骤2
步骤4: i++
步骤1-4 重复,直到i达到模式串最大长度
JAVA 代码实现:
public class KMP {
public static int kmp(String str,String pattern) {
int[] next = getNext(pattern);
int j = 0;
for(int i =0; i< str.length(); i++) {
while (j!=0 && str.charAt(i) != pattern.charAt(j)) {
//遇到坏字符时,查询next数组并改变模式串的起点
j = next[j];
}
if(str.charAt(i) == pattern.charAt(j)) {
j++;
}
if(j == pattern.length()) {
return i - j + 1;
}
}
return -1;
}
public static int[] getNext(String pattern) {
int[] next = new int[pattern.length()];
int j = 0;
next[0] = 0;
for(int i=2; i<pattern.length(); i++) {
//从next[i+1]的求解回溯到next[j]
while (j>0 && pattern.charAt(i-1) != pattern.charAt(j)) {
j = next[j];
}
if(pattern.charAt(i-1) == pattern.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
}
参考文章:https://baijiahao.baidu.com/s?id=1659735837100760934&wfr=spider&for=pc