菜鸡的KMP学习记录(0)
(这大概就是我的第一篇博客了吧)
- 个人理解
按照我目前的理解,KMP算法的主要原理的是通过Next数组来指导子串与模板串的匹配,即当子串与模板串在当前位置失配时,利用已知的子串信息,来尽可能地减少回溯,节省一部分时间。个人认为,整个算法的核心与精髓大概就是Next数组的求法了吧,所以我也主要记录一下我所理解的Next数组的原理与求法。 - Next数组
通过遍历一次子串,获取到的子串的具有某些特点信息(即:有哪些部分是与子串从头起部分是重复的),所获得数组。说到这种前后相重复的特征,我们就不得不提起(一个)两个概念,字符串的前缀与后缀。-
前缀与后缀
举个栗子有字符串“ABCBAB”,这时我们想要求他的Next数组。首先,我们需要从字符串的头部开始,一个一个地观察该字符串的子串,即:“A” , “AB” , “ABC” , “ABCB” , “ABCBA” , “ABCBAB”。我们先来观察"AB",它的前缀为"A",后缀为"B",然后,我们再看看"ABCB",他的前缀为:“A” , “AB” , “ABC”,后缀为:“B” , “CB” , “BCB”。不能看出,一个字符串的前缀就是一个字符串去掉最后一个字符,然后从头部开始依次取出余下字符所构成的字符串的集合。类似的,一个字符串的后缀就是一个字符串去掉第一个字符,然后从尾部开始依次取出余下字符所构成的字符串的集合。 -
Next数组求法
首先,明确一件事情,我们的Next数组是一个int类型的数组。其次,Next数组每一个位置所存的数值是我们每一个子串的前后缀中相等的最长串的长度。继续使用上面的例子:
“A”,前后缀都是空集,这时Next[0] = 0 ;
“AB”,前缀为"A",后缀为"B",这时前后缀中无相等元素,故Next[1] = 0;
“ABC”,前缀为:“A” , “AB”,后缀为:“BC” , “C”,这时前后缀中无相等 元素,故Next[2] = 0;
“ABCB”,前缀为:“A” , “AB” , “ABC”,后缀为:“BCB” , “CB” , “B”,这时前后缀中无相等元素,故Next[3] = 0;
“ABCBA”,前缀为:“A” , “AB” , “ABC” , “ABCB”,后缀为:“BCBA” , “CBA” , “BA” , “A”,这时前后缀中相等元素为"A"(长度为1),故Next[4] = 1;
“ABCBAB”,前缀为:“A” , “AB” , “ABC”,“ABCB” , “ABCBA”,后缀为:“BCBAB” , “CBAB” , “BAB” , “AB” , “B”,这时前后缀中相等元素为"AB"(长度为2),故Next[5] = 2;
这样我们就求得了一个简单字符串Next数组。
-
- 整体代码
void Next(string str,int* &next){
int i = 1, k = 0;
int m = str.length(); //获取字符串长度
next = new int[m];
next[0] = 0;
for (i = 1, k = 0; i < m; ++i){
while (k > 0 && str[i] != str[k]) { //此处大概是整个算法的精华吧
k = next[k - 1];
//简单的说就是如果自身失配了,next数组也可以利用
//已知信息指导自身进行一次回溯,即k的取值
//可能还是有些抽象,可以参考一下下面我盗的图
//也建议自己多写几个字符串看一下(亲身体会,有些帮助)
}
if (str[i] == str[k]){ //如果二者相等,最大相同前后缀长度++
k++;
}
next[i] = k;
}
}
int KMP(string str,string sub){
int n = str.length(), m = sub.length();
int loc = -1;
int i = 0, k = 0;
int* next = nullptr;
Next(sub,next); //获得子串的Next数组
for (i = 0, k = 0; i < n; ++i){ //从模板串头部开始检查
while (k > 0 && sub[k] != str[i]) {
k = next[k - 1];
}
if (sub[k] == str[i]){
k++;
}
if (k == m){ //此处我要保留的是子串在模板串中最后一次出现的位置
loc = i - m + 1;
//delete[] next; //养成释放空间的好习惯(大概算吧)
//return loc; //如果要第一次出现的位置可直接返回
}
}
delete[] next;
return loc;
}
- 总结
第一次写博客感觉很是生疏,本篇博客博客引用了一些该博主的博文内容,也十分感谢其内容对于我的学习指导(该博文地址)。写此篇以作学习记录,欢迎大佬们批评指正。