算法重新出发:1.KMP字符串匹配算法
自从前年疫情之后再没怎么刷题了,现在打算开始准备找实习找工作之类的,记录一下每天的进度。目前是在leetcode上刷题,每刷到忘记的知识点相关题会写一篇新的博客
- leetcode 28. 实现 strStr()
class Solution {
public:
int strStr(string haystack, string needle) {
int len1 = haystack.length(), len2 = needle.length();
if(len2==0) return 0;
if(len1==0) return -1;
int next[len2];
int j = 0, k = -1;
next[0]=-1;
while(j<len2-1){
if(k == -1 || needle[j] == needle[k]) next[++j] = ++k;
else k = next[k];
}
int h = 0, i = 0;
while(i < len1 && h < len2) {
if(h==-1 || haystack[i] == needle[h]) {
i++;
h++;
}
else h=next[h];
}
if(h >= len2) return (i-len2);
else return (-1);
}
};
KMP 算法主要是通过记录并运用模式串的重复部分消除主串指针的回溯来提高匹配的效率的。
对于每模式串 t 的每个元素
t
j
t_j
tj,都存在一个实数
k
k
k ,使得模式串
t
t
t 开头的
k
k
k 个字符(
t
0
,
t
1
…
t
k
−
1
t_0,t _1…t_{k-1}
t0,t1…tk−1)与
t
j
t_j
tj 前面的
k
k
k个字符(
t
j
−
k
,
t
j
−
k
+
1
…
t
j
−
1
t_{j-k},t_{j-k+1}…t_{j-1}
tj−k,tj−k+1…tj−1)相等,取最大的
k
k
k放入
n
e
x
t
next
next数组中,记
n
e
x
t
[
j
]
=
k
next[ j ]=k
next[j]=k。
n e x t next next数组的赋值过程如下
int next[len2];
int j = 0, k = -1;
next[0]=-1;
while(j<len2-1){
if(k == -1 || needle[j] == needle[k]) next[++j] = ++k;
else k = next[k];
}
k
k
k为
−
1
-1
−1即代表从头开始匹配
很容易就能理解if(needle[j] == needle[k]) next[++j] = ++k;
如图当模式串下标为
k
−
1
k-1
k−1的字符和下标为
j
−
1
j-1
j−1的字符相等的时候,
n
e
x
t
[
j
]
next[j]
next[j]值为
k
k
k,(意思是
j
j
j的前
k
k
k个字符与开头
k
k
k个字符相匹配,由于下标从0开始所以这里用到的是
k
k
k而非
k
−
1
k-1
k−1,由于是
j
j
j的前
k
k
k个字符不包含下标为
j
−
1
j-1
j−1的字符本身,所以给
n
e
x
t
[
j
]
next[j]
next[j]赋值)并且继续比较模式串下标为
k
k
k的字符和下标为
j
j
j的字符
那么if(needle[j] != needle[k])k = next[k];
是从哪来的呢?
由于不匹配,如图若比较模式串下标为
j
j
j的字符和下标为
k
k
k的字符,发现不匹配,将
n
e
x
t
[
k
]
next[k]
next[k]赋值给
k
k
k,不改动
j
j
j和
k
k
k,因为
n
e
x
t
next
next数组不包含本身的比较,所以下一次还要再判断模式串下标为
j
j
j的字符和改动后下标为
k
k
k的字符是否匹配