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

在上一篇博客中介绍了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算法还有改进的方法,以后会继续讨论。
《KMP算法与朴素模式匹配算法(C语言)》

欢迎指正

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/u011028771/article/details/52966977
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞