1.KMP是什么?
一种字符匹配算法
2.它的优点是什么?
快
3.它的应用范围?
判断子母串关系以及各种需要寻找子串的题目
4.KMP的运行过程?
输入母串——>输入子串——>计算子串的next数组——>匹配
5.KMP与传统匹配算法的区别?
KMP的匹配逻辑与传统匹配逻辑相同,不同的是,当遇到字符不相同时,KMP进行的操作是从(匹配时的第一个字符的位置+当前不匹配字符的next数组值)的位置开始与子串的第(不匹配字符的next数组值)个字符进行新一轮的匹配
6.关键next数组的含义?
next数组中储存的数值表示的是以当前字符为末尾(不包含当前字符),前面一部分子串的首尾相同字符个数,如:(初始数next[0]为-1)absdab的next数值为1,aba为0,abcba为0,abc为0,ababa为2.
7.如何形象的理解KMP算法?
传统的字符匹配是每当字符不符合时,整体从母串的下一个位置重头开始进行操作(a为母串,b为子串)
for(int i = 0; i < a.length; i++){
for(int j = 0, l = i; j < b.length; j++,l++){
if(b[j] != a[l]){
continue;
}
if(j == b.length - 1){
printf("%d\n", l - b.length);//输出匹配成功时的第一个字符在母串中的位置。
}
}
}
而KMP则根据不符合字符的next数组的值来确定需要移动多少个位置再继续匹配,其余操作并无不同。
for(int i = 0; i < a.length; i++){
for(int j = 0, l = i; j < b.length; j++,l++){
if(b[j] != a[l]){
i += next[j];
continue;
}
if(j == b.length - 1){
printf("%d\n", l - b.length);//输出匹配成功时的第一个字符在母串中的位置。
}
}
}
8.KMP算法的难点在哪?
经过上文的了解,我们发现貌似KMP也没有我们想象中的难,但其实不然,KMP算法的难点在于next数组的计算和匹配时的处理,细看之时,我们也可以发现next数组的计算貌似也是一个字符匹配的问题,因此我们需要在原来常规的匹配算法中进行改变,否则就无法达到我们提高程序运算效率的目的。
9.如何巧妙快速的计算next数组?
我们可以思考一下,当我们进行前后缀比较时是如何进行的,很明显,使用两个临时指针,一个指向头,另一个逐个后移匹配,当第一个字符匹配成功时,两者同时后移进行第二个字符的匹配,还需要考虑匹配完成时是否为末尾,否则不成立,继续进行原来的操作。
而next数组比较特殊,它需要得出每一个位置的最大前后缀的值,我们自然不能使用上面的传统方法,这与我们的初衷相悖。像这种连续操作的问题我们很容易想到循环和递归,接下来通过逐步分析来体会前人的智慧
字符串s = “ababaaab”;
初始化next[s.length - 1];
k = -1; j = 0;
next[0] = -1;
while(j < s.length - 1){
if (k==-1 || t.data[j]==t.data[k]){ //k为-1或比较的字符相等时
j++; k++;
next[j] = k;
}else{
k = next[k];
}
}
循环中的if比较是关键部分,循环以j的移动为循环运动过程,k的移动过程就是匹配过程,因为每一个next值都是当前字符串的最大前后缀的值(不包含当前字符),所以循环当移动到下一个字符时,k与j都是比较,的最新的字符,如果相符则加入最大前后缀中,然后继续进行循环也不需要从头匹配,如果不相符则将k移动到之前所求的最大前后缀的最后一个字符的next值所对应的字符串位置上,也是根据其最大前后缀(next[k])值进行位置判断,这就形成了一个递归模型。
如果还是觉得很抽象的话,大家可以根据代码,自己手写运算过程,就可以理解了。