您的位置:首页 > 其它

串的模式匹配算法:BF算法与KMP算法

2018-01-05 21:02 477 查看
下面两种算法都是根据如果具体例子讨论

主串MainString: acabaabcabcacaabc

模式串ModelString: abaabca

1、BF算法

1)思路

分别用i,j记录主串MainString和模式串ModelString中当前正在比较的字符位置。i 初始值为0,j初始值也为0

当两个串均未到达串尾时:

MainString[i] == ModelString[j],则++i;++j

MainString[i] != ModelString[j],则主串回到最初匹配的后一位——i = i - j + 1; 模式串回到起点——j = 0。

结束的标志:i或者j达到相应串末尾。若j>=模式串ModelString的长度,则表明匹配完成。返回第一个匹配的头部位置:i - j + 1; 若j < 模式串ModelString的长度,则不存在匹配结果,返回0。



2)代码:

int BF(char* MainString, char* ModelString) {
int LengthMain = strlen(MainString);
int LengthModle = strlen(ModelString);
if (LengthModle > LengthMain | LengthModle <= 0 | LengthMain <= 0) {    //不正常的情况直接退出
printf("please input correct string\n");
return -1;
}

int i = 0, j = 0;
while (i < LengthMain&&j < LengthModle) {
if (MainString[i] == ModelString[j]) {  //匹配,主串和模式串都向前进一
i++; j++;
}
else {  //不匹配,模式串从第一个字母开始,主串回到最初的字母的后一位
i = i - j + 1;
j = 0;
}
}
if (j >= LengthModle)                       //输入第一个匹配的位置,由于下标从0开始,所以加上1
return i - LengthModle + 1;
else
return 0;                               //没有匹配,则输出0
}


2、KMP算法

1)优化[b]BF的过程[/b]

在每次匹配过程中出现字符比较不相等时,不需要将i回溯(主串的标志),j也不必直接回溯到0,而是根据已经匹配的信息,尽可能将j向右滑动,然后再继续比较。例如BF中第二次匹配知道MainString[1] = ‘c‘,ModelString[0] = ‘a‘,这一次就没必要进行比较。

那么如何找出已经匹配过的规律呢。为了方便说明,这里数组下标均从1开始,代码实现时再说明下标从0开始的区别。我们假设MainString[i] != ModelString[j] 时,j需要回溯到k(k<j).根据以上信息可以得出两个已知条件:

MainString[i-j+1, i-j+2, …….., i-1] = ModelString[1, 2,……, j-1],这里我们为了方便与2.相结合,取其中的一部分:MainString[i-k+1, i-k+2, …….., i-1] = ModelString[j-k+1, j-k+2,……, j-1]——-(1)

MainString[i-k+1, i-k+2, …….., i-1] = ModelString[1, 2,……, k-1]——(2)

将(1)与(2)结合得:

ModelString[j-k+1, j-k+2,……, j-1] = ModelString[1, 2,……, k-1],翻译成书面语言就是:当ModelString[j]与MainString[i]不匹配时,回溯点kj直接存在一定的关系,从模式串ModelString第0位向右k-1个数与从模式串ModelString第j-1向左k-1个数完全匹配(两者方向都是从左到右)

因此回溯值只与模式串有关,与主串无关。可以根据模式串先求出每一个位置对用的回溯值。用Next[j]来存储所有回溯值。则给出Next[j]的定义:

哇,这个markdown不能表示分段函数,很蠢。只能献上我的丑字了。



由此可以得出模式串的Next数组:



但是这样的描述不适合计算机的计算,我们还应该再总结一下,使得Next[]方便计算机计算。

我们可以看出Next[]的出过程,相当于模式串ModelString中的子串与自己子串匹配。我们从关系式ModelString[ j-k+1, j-k+2,……, j-1 ] = ModelString[ 1, 2,……, k-1 ](即 Next[ j ] = k)出发,求Next[ j+1 ]:

ModelString[ j ] = ModelString[ k ]时,则ModelString[ j-k+1, j-k+2,……, j-1, j ] = ModelString[ 1, 2,……, k-1, k ]。易知,Next[ j + 1 ] = k +1;

ModelString[ j ] != ModelString[ k ]时,此时可以把求Next[]看成是一个模式匹配问题,其中模式串ModelString,主串也为ModelString,因为ModelString[1] = ModelString[j-k+1]; ModelString[2] = ModelString[j-k+2]; ………; ModelString[k-1] = ModelString[j-1]; 而ModelString[k] != ModelString[j],这时模式串ModelString中标位k要尽量向右移,而不是回溯到0(BF算法),根据以前的定义,回溯的标位为Next[k],记做k’(k’ = Next[k])。

(1). 若此时满足了,ModelString[k’] = ModelString[j],则根据1.可知,Next[j+1] = k’ + 1 = Next[k] + 1

(2).若此时ModelString[k’] != ModelString[j],则模式串还要进一步回溯。根据2.这一次回溯点k‘’ = Next[k’]…..找到满足的点ModelString[k*] = ModelString[j]然后转到(1)。否则一直回溯到k* = 1,转到(3)

(3).根据定义回溯到k* = 1代表不存在任何k*满足条件,因此,此时要将子串的第一位与主串j相比较,即Next[j+1] = 1.(这里说的第一位即下标为0的位置)

2)Next代码

void GetNext(char* ModelString, int *Next) {
int LengthModle = strlen(ModelString);
Next[0] = 0;
int j = 0, k = 0;               //k = Next[j]
while(j < LengthModle - 1){
if (k == 0 || ModelString[j] == ModelString[k - 1]) {
++j; ++k; Next[j] = k;
}
else
k = Next[k-1];
}
}


代码写出来跟上述公式不一样,原因是公式中为了方便描述,数组下标都是从1开始,而程序中数组下标均从0开始。对应着ModelString[j]指向的是正确的字符,但是j的数值少1,所以k的数值也要减一。所以

判断条件ModelString[ j ] != ModelString[ k ] ===》 ModelString[ j ] != ModelString[ k-1 ]

1.Next[j+1] = k + 1 ===》 Next[j] = k

2.k = Next[k] ===> k = Next[k-1]

3)主串[b]MainString与模式串ModleString匹配[/b]

MainString[i] == ModelString[j]时,双方都向右移一位,++i,++j

MainString[i] != ModelString[j],模式串需要回溯。j = Next[j-1],如果j回溯到0,转3

j = 0,说明ModelString[0] != MainString[i],下一步需要将ModelString[0]与MainString[i+1]比较。只需要执行++i即可

当i , j 超过对应串的串长时,算法终止。若j>=模式串ModelString的长度,则表明匹配完成。返回第一个匹配的头部位置:i - j + 1; 若j < 模式串ModelString的长度,则不存在匹配结果,返回0。

4)KMP代码

int KMP(char* MainString, char* ModelString) {
int LengthMain = strlen(MainString);
int LengthModle = strlen(ModelString);
if (LengthModle > LengthMain | LengthModle <= 0 | LengthMain <= 0) {
printf("please input correct string\n");
return -1;
}

int *Next = (int *)malloc(LengthModle * sizeof(int));
GetNext(ModelString,Next);

int i = 0, j = 0;
while (i < LengthMain&&j < LengthModle) {
if (MainString[i] == ModelString[j]) {
++i; ++j;
}
else {
if (j != 0) {
j = Next[j - 1];
}
else {
++i;
}
}
}
if (j >= LengthModle)
return i - LengthModle + 1;
else return 0;
}


3、例子

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int BF(char* MainString, char* ModelString);
int KMP(char* MainString, char* ModelString);
void GetNext(char* ModelString, int *Next);

int main() {

char* MainString = "acabaabcabcacaabc";
char* ModelString = "abaabca";
printf("The result of BF : %d\n",BF(MainString, ModelString));
printf("The result of KMP : %d\n", KMP(MainString, ModelString));

//printf("%s", MainString);
//printf("%d", strlen(s));
return 0;
}

int BF(char* MainString, char* ModelString) { int LengthMain = strlen(MainString); int LengthModle = strlen(ModelString); if (LengthModle > LengthMain | LengthModle <= 0 | LengthMain <= 0) { //不正常的情况直接退出 printf("please input correct string\n"); return -1; } int i = 0, j = 0; while (i < LengthMain&&j < LengthModle) { if (MainString[i] == ModelString[j]) { //匹配,主串和模式串都向前进一 i++; j++; } else { //不匹配,模式串从第一个字母开始,主串回到最初的字母的后一位 i = i - j + 1; j = 0; } } if (j >= LengthModle) //输入第一个匹配的位置,由于下标从0开始,所以加上1 return i - LengthModle + 1; else return 0; //没有匹配,则输出0 }

int KMP(char* MainString, char* ModelString) { int LengthMain = strlen(MainString); int LengthModle = strlen(ModelString); if (LengthModle > LengthMain | LengthModle <= 0 | LengthMain <= 0) { printf("please input correct string\n"); return -1; } int *Next = (int *)malloc(LengthModle * sizeof(int)); GetNext(ModelString,Next); int i = 0, j = 0; while (i < LengthMain&&j < LengthModle) { if (MainString[i] == ModelString[j]) { ++i; ++j; } else { if (j != 0) { j = Next[j - 1]; } else { ++i; } } } if (j >= LengthModle) return i - LengthModle + 1; else return 0; }

void GetNext(char* ModelString, int *Next) { int LengthModle = strlen(ModelString); Next[0] = 0; int j = 0, k = 0; //k = Next[j] while(j < LengthModle - 1){ if (k == 0 || ModelString[j] == ModelString[k - 1]) { ++j; ++k; Next[j] = k; } else k = Next[k-1]; } }


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 C