1.BF算法
BF算法也叫朴素算法,就是字面意思,简单粗暴的将主串和字串进行匹配
当开始匹配 的时候,i和j同时往后跑, 一直到发生失配:
匹配到这一步是的时候发生失配,失配之后字串回到头部,而主串回到 i - j + 1的位置处
也就是说,i必须回退到这次失配发生时的起点的下一个位置。
然后继续匹配,重复上面的过程。一直到匹配成功或者主串遍历完都没有匹配到结果的话,就结束。
可以看到,BF算法的时间复杂度很高,通常情况下他的时间复杂度为O(n*m),也就是O(n^2),其中n为主串的长度,m为字串的长度。
虽然时间复杂度很高,但是BF算法的思想很容易理解,代码写起来也比较容易:
int BF_string()
{
const char* s = "abcabcdabcde";
const char* t = "bcda";
int slen = strlen(s);
int tlen = strlen(t);
int i = 0;
int j = 0;
while (i < slen&&j<tlen)
{
if (s[i] == t[j])
{
++i;
++j;
}
else
{
i = i - j + 1;
j = 0;
}
}
if (j >=tlen)
{
return i-j;
}
return -1;
}
2.KMP算法
由于BF算法的时间复杂度略高,所以就诞生了KMP算法。
KMP算法与BF算法不同的地方在于,它不再是暴力求解,并且它的时间复杂度要远远低于BF算法。这是因为在KMP算法中,指向主串的i不用回退,所以提高了匹配的效率。可以说 它是对BF算法的一种改进
前面都和BF算法一样,但是当发生失配时, 就会发生俩种情况:
(1).第一种:失配时,子串前方没有相同的俩个字符或字符串,就像上面的图举得例子一样,字串前几个元素俩俩互不相同,但是字串前面那几个元素已经和主串进行过比较了并且是相同的,所以主串的前几个元素也是俩俩不相同。这个时候就 没有必要去回退 i 了,因为即使 i 回退了,也是没有效果的,前面那几个元素都俩俩不相同,i 的回退就没有意义了,无论怎么匹配都会失配的。
(2).第二中情况就是发生失配时,字串前面有字符或字符串相等,并且相等的俩个字符串必须是:一个字符串的头为字串的头,另一个字符串的尾紧挨着发生失配的地方。比如这种:
发生失配时的第二种情况。这个时候,按理来说i 应该需要回退,但是在KMP算法中,它使用j的回退来代替了i的回退。本来i 应该回退到第二个a位置,然后j去回退到头,但是这样再去比较的话,后俩个字符一定是 匹配成功的,所以干脆让i不动,j 回到头后自己向后走俩步,也就是回退到第二个a位置。
所以,KMP算法的难点就在于如何去计算j 应该回退的位置。每个j在发生失配的时候都有一个自己和回退位置,这个位置可以保存在next数组中,那么如何去求next数组就是个难题。
next数组是只与字串本身有关 的一个数组,和主串无关。每个字符串都有一个对应的next数组。
可以设计这样一个算法:刚开始时令 j 指向字串中第 1 个字符(j=1),i 指向第 2 个字符(i=2)。接下来,对每个字符做同样的操作:
如果 i 和 j 指向的字符相等,则 i 后面第一个字符的 next 值为 j+1,同时 i 和 j 做自加 1 操作,为求下一个字符的 next 值做准备;
如果 i 和 j 指向的字符不相等,则执行 j=next[j] 修改 j 的指向,然后以同样的方法对比 i 和 j 指向的字符,以此类推。当 j 的值为 0 时,将 i 后面第一个字符的 next 值置为 1。
再将i换成 j +1;
代码实现起来也比较好理解:
int* Get_next(const char* t)
{
int len = strlen(t);
int* p = new int[len] {};
int j = 1;
p[0] = -1;
if (len > 1)
{
p[1] = 0;
}
while (j + 1 < len)
{
if (t[j] == t[p[j]])
{
p[j + 1] = p[j] + 1;
}
else
{
p[j + 1] = 0;
}
++j;
}
for (int i = 0; i < len; ++i)
{
cout << p[i] << " ";
}
return p;
}
int KMP_string()
{
const char* s = "abcabcdabcde";
const char* t = "aabbaac";
int slen = strlen(s);
int tlen = strlen(t);
int i = 0;
int j = 0;
int* next = Get_next(t);
while (i < slen && j < tlen)
{
if ((j==-1)||s[i] == t[j])
{
++i;
++j;
}
else
{
j = next[j];
}
}
if (j >= tlen)
{
free(next);
return i-j;
}
free(next);
return -1;
}
感谢阅读!