KMP算法简介
在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个字符串S内查找一个词W的出现位置。一个词在不匹配时本身就包含足够的信息来确定下一个匹配可能的开始位置,此算法利用这一特性以避免重新检查先前匹配的字符。(参考维基百科)
KMP算法实现与代码
1 、 以力扣28. 实现 strStr()为例
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
示例 3:
输入:haystack = “”, needle = “”
输出:0
2、算法实现
虽然暴力算法可以很快实现该问题,但是时间复杂度太高,针对于字符串匹配问题,可以使用KMP算法来降低复杂度。利用已经匹配过的信息,然后回溯到合适位置,这样就不需要从头开始匹配。这主要是利用next前缀表(数组)来记录最大字串,根据next表来回溯到相应的位置。
2、1 next表建立
KMP算法最重要的一步就是建立next表,当字符串不匹配时候,可以根据next表来寻找回溯位置。next表其实就是寻找共同的前后缀。
例如字符串s = “aabaabaaaf”,其对应的next表为next[10] = {0, 1, 0, 1, 2, 3, 4, 5, 2, 0};它是如何来的呢?
- 建立一个前指针front = 0,和尾指针end = 1,用来寻找相同的前后缀。
- 如果是s[front] == s[end],那么front++,end++;
- 如果front > 0 && s[front] != s[end],那么就需要回溯到上一次的最长相同前后缀,即front = s[front - 1],直到front == 0 或者 s[front] == s[end];
- 如果front = 0, 并且 s[front] != s[end],那么end++;
- 如果front = 0, 并且 s[front] == s[end],那么front++,end++;
// i为前指针,j为尾指针
for(int i = 0, j = 1; j < needleSize; j++) {
while(i > 0 && needle[i] != needle[j]) {
// 当发现前后缀不一样的时候,需要回溯
i = next[i - 1];// 回溯到上一个字母的最长相同前后缀,然后继续判断
}
if(needle[i] == needle[j]) {
i++;// 前指针i+1,因为尾指针j在for循环时会自动+1,所以不需要处理
}
next[j] = i; //如果i = 0,那么没有相同前后缀,next[j] = 0,尾指针要加1继续寻找
}
2、2 寻找字串位置
class Solution {
public:
int strStr(string haystack, string needle) {
int haystackSize = haystack.size(); //
int needleSize = needle.size();
if(needleSize == 0) {
return 0;
}
if(haystackSize < needleSize )
return -1;
int next[needleSize];// next表
next[0] = 0;
for(int front = 0, end = 1; end < needleSize; end++) {
while(front > 0 && needle[front] != needle[end]){
// 当前缀和后缀不同时,需要回溯,根据已建立的next表来回溯
front = next[front - 1];// 直到front = 0或者needle[front] == needle[end]
}
if(needle[front] == needle[end])
front++;
next[end] = front;
}
// 查找字串的位置
for(int i = 0, j = 0; i < haystackSize; ) {
if(j > 0 && haystack[i] != needle[j]){
j = next[j - 1];
continue;
}
else if(j == 0) {
if(haystack[i] != needle[j]) {
i++;
} else {
i++;
j++;
}
}
else {
i++;
j++;
}
if(j == needleSize )
return i - j;
}
return -1;
}
};