什么是KMP算法:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为T),如果它在一个主串(接下来称为S)中出现,就返回它的具体位置,否则返回-1
字符串的前缀和后缀:
- 前缀:所有以第一个字符开头的连续字符串(不包括最后一个字符,例:T的一个前缀aabaa)。
- 后缀:以最后一个字母结尾的连续字符串(不包括第一个字符,例:T的一个后缀 abaaf)。
前缀表:
前缀表记录下标i之前(包括i)的字符串中,最大长度的相同前缀后缀。如何计算前缀表,例:T中以下标为4的字符结尾的字符串aabaa的最长相同前后缀为aa,长度为2,所以next[4]=2。
前缀表和next数组的关系:
next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。这里采用前缀表做为next数组。
匹配过程:
用代码实现求next数组
1.初始化
定义两个指针i,j,i表示指向后缀末尾,j表示指向前缀末尾,j也表示最长相等前后缀的长度。
对next数组初始化next[0]=0;j=0;i=1;(为什么i从1开始呢?因为从0开始的话,字符串a没有前后缀,这也是next[0]赋值为0的原因。)
2.处理前后缀不相同的情况
3.处理前后缀相同的情况
4.更新next数组的值
图片
KMP算法代码
leetcode 28. 实现 strStr()https://leetcode.cn/problems/implement-strstr/
//kmp算法
public int strStr2(String haystack, String needle) {
if (needle.length()==0)return 0;
int[] next=new int[needle.length()];
//求next数组
int j=0;
next[0]=0;
//i表示后缀的末尾,j表示前缀的末尾
for (int i = 1; i < needle.length(); i++) {
while (j>0 && needle.charAt(i)!=needle.charAt(j)){
j=next[j-1];
}
if (needle.charAt(j)==needle.charAt(i)){
j++;
}
next[i]=j;
}
//匹配字符串
j=0;
for (int i=0;i<haystack.length();i++){
while (j>0 && needle.charAt(j)!=haystack.charAt(i)){
j=next[j-1];
}
if (needle.charAt(j)==haystack.charAt(i)){
j++;
}
if (j==needle.length()){
return i-needle.length()+1;
}
}
return -1;
}