今天刷到KMP算法
具体的力扣28 找出字符串中第一个匹配项的下标,写篇文章方便自己后续复习
例如文本串aabaabaaf ,模式串aabaaf。在匹配过程中我们在f这里不匹配了,那么我们就看f的前面。
前面是模式串的子串,子串的后缀是aa,我们就找到与这个后缀相等的前缀的后面——也就是b。所以我们要知道最长相等前后缀,这样我们在遇到不匹配的位置的时候呢,我们就找前面的这个子串的最长相等前后缀。
aabaa的最长相等前后缀就是2,所以跳到b。
那么什么是前缀,什么又是后缀,拿aabaaf举例。
前缀是包含首字母不包含尾字母的所有字母,所以有如下:
a
aa
aab
aaba
aabaa
aabaaf就不是前缀,不包含尾字母。
后缀是只包含尾字母,不包含首字母:
f
af
aaf
baaf
abaaf
知道了前缀和后缀,我们要求模式串子串的最长相等前后缀。
其中a aa aab aaba aabaa 都是模式串的子串,我们就来找子串的最长相等前后缀
a -> 0
aa-> 1 (a)
aab -> 0
aaba -> 1 (a)
aabaa -> 2 (aa)
aabaaf -> 0
由此我们就得到了这个前缀表 010120
文本串: a a b a a b a a f
模式串: a a b a a f
前缀表: 0 1 0 1 2 0
当我们在f不匹配后,我们要找f前面的最长相等前后缀 也就是2,2意味着这里有个后缀aa,前面也有一个相等的前缀aa。如果我们在后面不匹配了冲突了,那么我们就要找到与其相等的前缀aa的下一个元素重新开始匹配。那么这个重新开始匹配的元素坐标是多少呢,其实就是其最长相等前后缀的长度.这就是前缀表的作用。
那么前缀表怎么求呢 这里直接给出代码:
int m = pattern.size();
vector<int> next(m,0);
//这里的i是模式串遍历的指针,max_len是最长相同前后缀的长度
int i = 1;
int max_len = 0;
//下面我们开始遍历,前指针为最长相同前后缀的指针,后指针为我们遍历模式字符串的指针
while(i < m){
//如果遍历的时侯发现后指针与前指针的字符相同,我们就把max_len+1,代表后面的内容跟前面的内容相同。
if(pattern[i] == pattern[max_len]){
max_len++;
i++;
next[i] = max_len;
}
else{
//如果遍历时候发现后指针与前指针的字符不同,那么看max_len的数值,
//如果为0则代表前面没有跟现在的字符内容一样的,可以直接赋值0然后i++
if(max_len == 0){
next[i] = 0;
i++;
}
//如果max_len的数值不为0,也就是现在的else情况:那么说明后指针的前一个字符跟第一个字符相同
//(max_len=1,如果后指针的前两个字符跟开头的两个字符相同那就是max_len=2),所以我们要回退到当前指针的前一个字符,
//来看前一个字符的最长相等前后缀。一定要注意这里i并不+1,i需要重新while循环来判断,例如下面 当后指针指到最后一个B的时候max_-len = next[3-1] =1,然后再次循环pattern[7] == pattern[1] 这是成立的,然后max_len++,i++ 结束循环。
else{
max_len = next[max_len-1]
}
//例子中子串与前缀表的对应关系。
A B A C A B A B
0 0 1 0 1 2 3 0
构建完next数组后,我们在根据next数组进行遍历就可以了,这里也分为主串指针和子串指针。
主串为haystack 子串为needle
int master = 0;
int sub = 0;
while(master < haystack.size()){
if(haystack[master] == needle[sub]){
sub++;
master++;
}
//每相加一次都判断下是否把子串遍历结束了防止越界
if( sub == strlen(needle){
return master-sub;
}
//当主串指针和子串指针不相等时,先检查是否在子串第一个字符就不相等
//sub >0说明不是在子串第一个字符就不相等,这时候next数组就派上用场了。直接把子串指针转移到前一个字符的最长相同前后缀
else if(haystack[master] != needle[sub]) && ( master < n)){
if(sub > 0){
sub = next[sub-1];
}
//这里就是在子串第一个字符就不相等了,直接主串++就OK
else{
master++;
}
}
}
//如果sub的值等于子串的长度,那么说明sub已经遍历整个子串了,所以就返回master-sub,这个值是在主串中第一次出现子串的位置。
到这里KMP相关的next和遍历就结束了,如果后续在看不懂可以直接看这个视频 配合视频食用更佳。
https://www.bilibili.com/video/BV1AY4y157yL/?share_source=copy_web&vd_source=dd22477739333dfa4121f1203f4948c9