1. BF
BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。
这里是BF是很常见的手法,根据题目要求大家一般自己就可以实现,难度不大,所以我们也不过多解释,这里可以说一下算法思想:
当目标串S=“aaaaab”,模式串T=“aaab”时,S的长度为6,T的长度为4。
S与T会开始匹配,i=1,j=1时,a与a相等,那么++i,++j。当i与j加到4时,不相等了,就让i回溯到i=2,j=1时,又开始一轮配对,直到完成。
2. KMP
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) 。
2.1 算法思想
KMP当中的i不用回溯,j也不用从头开始。例如目标串S=“ababcabcacbab”,模式串T=“abcac”。
可以显然看出比较到第3个字符时不一样,
这时候我们不让i回溯,也即是i不变,就从第3个字符开始比较,S中匹配到第7位字符时匹配不成功,这时i仍然不回溯,
而在T中,j有时不必回到头上,因为当前比较的字符c前面的字符a是已经比较过的,而且与第一个字符又相同,那么相当于第一个字符已经比较过了,所以这时我们的j可以从第2个字符开始匹配。
通过“i的不回溯和j的不一定是从第一个元素开始匹配”,KMP算法才能够提速,才能够让时间复杂度为O(n+m)。
小伙伴们可能开始疑惑了,j到底要怎么去确定它的位置呢?
那么就请跟谁我的脚步,去追寻KMP算法的核心吧!
2.2 next——j的下一个位置
j到底要回到什么位置呢?从第几个字符开始呢?这样的两个问题我们称之为j的下一个位置,用next[j]这样一个数组来表示,那next[j]有是怎么算的呢?
2.2.1 next的思想
以下内容只与模式串有关系
如图所示:
当j=1时,next[j]=0;当j!=1时,我们首先看第一种,第一个意思是在模式串中当从头开始到k-1的位置上的字符与j前面的k-1个字符匹配时(为同一个字母),k的值不止一个,所以我们就让next[j]=max(k);其他情况就让next[j]=1。
有的小伙伴又疑问了,为什么有这样一个取值情况呢?其实这个为什么不需要我们掌握,研究起来也很麻烦,如果有小伙伴想得通的话,欢迎评论区留言,这里我们就不做过多解释。
还有的小伙伴还是没看懂我写的,不要着急,下面我们来看例题,看完之后就把相对于抽象的东西具体化啦!
2.2.2 例题
这里我们先介绍一下串的前缀与后缀。
如果一个字符串是abcd,那么它的前缀有a、ab、abc,后缀有d、cd、bcd,前缀后缀都不包括其本身。
介绍这个呢是因为你可以看到p1…pk-1就是字符串的前缀,而pj-k+1…pj就是字符串的后缀。
好了,下面是我们的模式串。
我们很容易知道 当j=1时,next[j]=0;
那么当j=2时,我们看到字符b前面的字符a,a既不是前缀也不是后缀,所以属于其他情况next[j]=1;
当j=3时,我们看到字符前面的字符串ab,a!=b,即前缀!=后缀,所以属于其他情况,那么next[j]=1;
当j=4,a前面的字符串abc,a!=c、ab!=bc,即前缀!=后缀,属于其他情况,next[j]=1;此时如图
当j=5时,注意,5位置对应的字符a前面的字符串为“abca”,有a=a,但ab!=ca、abc!=bca,所以只有一个字符a相等,也就是说有k-1个字符是一样的,故 k-1=1,即k=2,此时是唯一的k,也是最大的k,所以next[j]=max(k)=2;
j=6时,字符串 abcaa中,a=a、ab!=aa、abc!=caa、abca!=bcaa,所以k-1=1,即k=2,next[j]=max(k)=2;
j=7,字符串 abcaab中,a!=b、ab=ab、abc!=aab、abca!=caab、abcaa!=bcaab,此时有2个字符相等,所以k-1=2,即k=3,则next[j]=max(k)=3;
再看一下这个图
j=8,容易发现字符串abcaabb中前缀与后缀无一相等,此时j!=1,所以属于其他,则next[j]=1;
j=9,情况与j=8一样,next[j]=1;
j=10,字符串abcaabbca中,a=a,所以k=2,next[j]=2;
j=11,字符串abcaabbcab中,ab=ab,所以k-1=2,next[j]=max(k)=3;
j=12,字符串abcaabbcabc中,abc=abc,所以k-1=3,next[j]=max(k)=4;
…
最终情况如下图
看到这里相信大部分小伙伴都理解到了吧!
那么我们的代码如何实现呢?
2.3 代码实现——c语言
用的c语言编写,也没有分文件编写,代码不多,希望大家多多支持!
#incude<stdio.h>
int length(char *a)
{
char *b=a;
int i=1;
while(b[i]!=NULL)
{
++i;
}
return i;
}
int Index_KMP()
{
char s[]="ababcabcacbab";
char t[]="abcac";
int a=1,b=0;
int next[20];
next[1]=0 ; //next数组是从下标为1开始的
while(a<length(t)) //实现next的变化过程
{
if(b==0||t[a]==t[b])
{
++a;++b;
next[a]=b;
}
else
b=next[b];
}
int i=1,j=1;
while(i<length(s)&&j<length(t))
{
if(j==0||s[i]==t[j]) //恰好完全匹配
{
++i;++j;
}
else
j=next[j];//i不变,j后退
}
if(j>=length(t))
return i-length(t);//匹配成功,返回结束位置
else
return -1; //返回不匹配的标志
}
int main()
{
printf("%d",Index_KMP());
return 0;
}
以上是基础版的KMP,如果当我们的模式串是“ aaaaaab”这样的,其实我们还有更简便的方法,大家可以自己去思考一下。
有什么不正确的地方欢迎一起讨论指正,hhhh |