串匹配@BF算法@KMP算法
前言
在日常生活中,文本编辑器,数据库检索,C++模板匹配,模式识别等等都需要使用到字符串匹配,来检索到我们需要查找的东西。
这里涉及到一个源串和子串的问题。
我们设定一个源串:BBC ABCDAB ABCDABCDABDE
子串:ABCDABD
一、BF算法
BF算法也可以称作为暴力算法,思想是自左而右,以字符为单位,依次移动模式串,直到某个位置发生匹配。
若不匹配,子串的位置设为0,源串从上一次匹配位置向后移动一个单位。即不匹配成功,又开始重头匹配。
如图,不匹配,源串位置向后移动,子串位置从0开始。这个方法很直观,但是若源串长度为m,子串长度为n,则需要进行(m - n) * n次比较,最差情况下n = m / 2,此时间复杂度为O(m*m / 4),即时间复杂度为O(m * m)。
int searchString(const char *src, const char *sub) {
int srcLen = strlen(src);
int subLen = strlen(sub);
int srcIndex = 0;
int subIndex = 0;
for (; srcIndex < srcLen - subLen + 1; srcIndex++) {
for (subIndex = 0; sub[subIndex]; subIndex++) {
if (src[srcIndex + subIndex] != sub[subIndex]) {
break;
}
}
//判断是否匹配到了字符串,匹配到返回源串中对应的下标。
if (0 == sub[subIndex]) {
return srcIndex;
}
}
return NOT_FOUND;
}
二、KMP算法
KMP算法是基于BF算法提出来的,为了减少字符串的回溯,在不匹配字符位置,若源串和子串有匹配字串,直接进行对其对齐处理,源串不需要再进行一项一项移动。
KMP算法的思想就是:在已匹配的前缀当中寻找到最长可匹配后缀子串和最长可匹配前缀子串,在下一轮直接把两者对齐,从而实现模式串的快速移动。
在开始KMP算法时,我们先介绍next数组,这是KMP的基础。
next数组是先对字串进行遍历,构建next数组。next实际上就是在已匹配的前缀当中寻找到最长可匹配后缀子串和最长可匹配前缀子串。说不多说,直接上图。
给出next数组的算法。
int *getNext(const char *sub, int subLen) {
int index = 2;
int j = 0;
int *next = NULL;
next = (int *) calloc(sizeof(int), subLen);
while (sub[index]) {
if (sub[index-1] == sub[j]) {
next[index++] = ++j;
} else {
if (j == 0) {
next[index++] = j;
} else {
j = next[j];
}
}
}
return next;
}
而KMP算法则是在next数组基础上,利用next数组的j的位置,决定下一次的next数组的位置。
int kmpMatch(const char *src, const char *sub) {
int srcLen = strlen(src);
int subLen = strlen(sub);
int i = 0;
int j = 0;
int *next;
next = getNext(sub, subLen);
while (sub[j] && srcLen - i >= subLen - j) {
if (src[i] == sub[j]) {
i++;
j++;
continue;
} else if (j != 0) {
j = next[j];
} else {
i++;
}
}
free(next);
return sub[j] ? NOT_FOUND : i - j;
}
三、总结
直到现在我也还没完全弄懂KMP算法,等到完全理解后,会重新再写一篇的。