您的位置:首页 > 编程语言 > C语言/C++

KMP算法与朴素模式匹配算法(C语言)

2016-10-29 21:57 411 查看
在上一篇博客中介绍了KMP算法和朴素模式匹配算法的区别,本文主要针对这两种算法的C语言实现进行讲解。

#include<stdio.h>
#define OK 0
#define ERROR -1
#define FAILED 1
int readFile(char **buffer) {
FILE *fp;
int length;
int error;
fp = fopen("readFile.txt", "rt");
if (fp == NULL) {
printf("open file failed!\n");
return ERROR;
}
fseek(fp, 0, SEEK_SET);
fseek(fp, 0, SEEK_END);
length = ftell(fp);
(*buffer) = (char *)malloc(length);
if ((*buffer) == NULL) {
printf("malloc failed!\n");
return ERROR;
}

fseek(fp, 0, SEEK_SET);
error = fread((*buffer), sizeof(char), length / sizeof(char), fp);
if (error == 0 ) {
printf("Read file failed!\n");
return ERROR;
}
fclose(fp);
return OK;
}
//朴素的模式匹配算法
int index(char *buffer, char *check, int *inx,
int sizeBuffer,int sizeCheck) {
int i = *inx;
int j = 0;
while (i < sizeBuffer && j < sizeCheck) {
if (buffer[i] == check[j]) {
i++;
j++;
}
else {
i = i - j+1;
j = 0;
}
}
if (j == sizeCheck)
*inx = i - sizeCheck;
else
return FAILED;
return OK;
}
//KMP模式匹配算法
//计算next数组
int compNext(char *check, int *next, int sizeCheck) {
int j = 1;
int i = 0;
int count = 0;
while (j < sizeCheck-1) {
if (check[i] == check[j]) {
i++;
count++;
j++;
next[j] = count;
}
else {
count = 0;
if (i == 0) {
j++;
next[j] = i;
}
else
i--;
}
}
//  for (i = 0; i < sizeCheck; i++) {
//      printf("%d", next[i]);
//  }
return OK;
}
//KMP字符匹配
int index_kmp(char *buffer, char *check,
int sizeCheck, int sizeBuffer, int *inx) {
int i = *inx;
int j = 0;
int flag = 0;
int count = 0;
int next[5] = { 0 };
compNext(check, next, sizeCheck);
while (i <sizeBuffer && j<sizeCheck) {
if (buffer[i] == check[j]) {
i++;
j++;
}
else {
if (j == 0)
i++;
j = next[j];
}
}
if (j == sizeCheck)
*inx = i - sizeCheck;
else
return FAILED;
return OK;
}
int main() {
int error;
char *buffer;
char checkChar[6] = "aware";
int i = 0;
int flag = 0;
int count = 0;
int inx = 0;
int sizeCheck = sizeof(checkChar)/sizeof(char)-1;
error = readFile(&buffer);
if (error == ERROR) {
getchar();
}
while (buffer[i] != '\0') {
for (int j = 0; j < sizeCheck; j++) {
if (checkChar[j] == buffer[i + j])
flag++;
}
if (flag == 5)
count++;
flag = 0;
i++;
}
printf("The total number is %d\n", count);
printf("The local of them are:\n");
while (error != 1) {
error = index(buffer, checkChar, &inx, i, sizeCheck);
if (error == OK) {
printf("%d\n", inx);
}
inx = inx + 4;
}
inx = 0;
error = 0;
printf("The local of them are:\n");
while (error != 1) {
error = index_kmp(buffer, checkChar, sizeCheck, i,&inx);
if (error == OK) {
printf("%d\n", inx);
}
inx = inx + 4;
}
free(buffer);
return 0;
}


本文的代码主要包含120行到130行,求取该文本中aware的个数;132行到138行,利用朴素模式匹配方法求着8个单词的位置;142行到148行利用KMP算法求8个单词的位置;

感兴趣的朋友可以利用朴素匹配方法和KMP算法求单词总数;

下面针对代码进行说明:

朴素模式匹配方法:

int index(char *buffer, char *check, int *inx,
int sizeBuffer,int sizeCheck) {
int i = *inx;
int j = 0;
while (i < sizeBuffer && j < sizeCheck) {
if (buffer[i] == check[j]) {
i++;
j++;
}
else {
i = i - j+1;
j = 0;
}
}
if (j == sizeCheck)
*inx = i - sizeCheck;
else
return FAILED;
return OK;
}


跳出while循环的条件有两个,i大于等于文档字符总数(sizeBuffer)或者j大于匹配字符串字符总数(本文是5:aware)。

当有5个字符连续匹配成功,则j为5,跳出循环;*inx = i - sizeCheck;计算出单词起始位置。

inx表示开始查询的位置;

KMP算法

next数组的求取

//计算next数组
int compNext(char *check, int *next, int sizeCheck) {
int j = 1;
int i = 0;
int count = 0;
while (j < sizeCheck-1) {
if (check[i] == check[j]) {
i++;
count++;
j++;
next[j] = count;
}
else {
count = 0;
if (i == 0) {
j++;
next[j] = i;
}
else
i--;
}
}
//  for (i = 0; i < sizeCheck; i++) {
//      printf("%d", next[i]);
//  }
return OK;
}


请注意这里的i和j与上一篇博文中的i和j意义不同,上一篇文章中的i表示文本的索引;j表示匹配字符串的索引;这篇文章中i表示匹配字符串前缀的索引,j表示后缀的索引;

j的取值最大为当前字符的前一个字符,所以为aware中j最大取到r及j=0到j=3;所以j小于sizeChar.

当check[i] == check[j]成立,count增加,同时i和j继续增加;

若不成立

说明字符不是连续相等,计数器count清零;

此时,如果前缀索引为0及第一个字母,则后缀需要向后增加一个字符;否则后缀不变,前缀向前移动一个字符;

关于i的回溯;

举个例子:

ababaaba

第一个字符和第二个字符不相等;

那么以后无论哪个字符从第二个字符b开始的后缀都不可能和前缀相等;以为第二个b开始的后缀对应的是第一个a开始的前缀;

此时最大的前后缀只能是以第三个a开始的后缀;

本程序的回溯存在一些问题,但是目前测试的字符串计算的结果都是正确的,以后发现更好的回溯方法再更正,欢迎指正;

关于next数组的求取办法及详细代码实现分析和i的回溯问题请查看如下博客:

http://blog.csdn.net/u011028771/article/details/52993198

http://blog.csdn.net/u011028771/article/details/52966473

KMP程序

//KMP字符匹配
int index_kmp(char *buffer, char *check,
int sizeCheck, int sizeBuffer, int *inx) {
int i = *inx;
int j = 0;
int flag = 0;
int count = 0;
int next[5] = { 0 };
compNext(check, next, sizeCheck);
while (i <sizeBuffer && j<sizeCheck) {
if (buffer[i] == check[j]) {
i++;
j++;
}
else {
if (j == 0)
i++;
j = next[j];
}
}
if (j == sizeCheck)
*inx = i - sizeCheck;
else
return FAILED;
return OK;
}


仔细观察就会发现,朴素匹配是

else {
i = i - j+1;
j = 0;
}


而KMP算法是

else {
if (j == 0)
i++;
j = next[j];
}


可以看到二者的区别是i值得变化;朴素匹配法回溯的是i值;KMP算法回溯的是j值;所以对于匹配字符串中相同的字符串比较多时,KMP算法的效率会优于朴素匹配;若匹配字符串中字符全部不相同,KMP算法优势并不明显。

关于KMP算法还有改进的方法,以后会继续讨论。



欢迎指正

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