数据结构之串的模式匹配(C语言实现)

来源:互联网 发布:怎样加入淘宝 编辑:程序博客网 时间:2024/05/21 23:09

一、暴力匹配算法(BF)

    BF全称为Brute-Force,最简单直观的模式匹配算法。
1.算法思想
两个字符串进行匹配时,一个主串和一个模式串,就是按照我们最容易想到的算法来进行匹配。用两个变量i,j分别记录主串和模式串的匹配位置,如果两者在某个字符不匹配,则将记录主串匹配位置的变量i回退到匹配前i的位置的后一个位置,将j回头到模式串的第一个字符位置;如果i和j指向的主串和模式串字符匹配,则将两者同时向后走一个位置,继续比较;最后,如果j的位置已经走过了模式串的所有字符,则说明此时匹配成功。这个算法很简单,也容易理解,下面直接给出代码。
2.代码实现
#include<stdio.h>#include <string.h>int BF(const char *MainStr, const char *PatternStr){    int i = 0;   //主串的匹配起始位置    int j = 0;   //模式串的匹配起始位置    int iMainLen = strlen(MainStr);     //主串长度    int iPatLen = strlen(PatternStr);   //模式串长度    while (i < iMainLen && j < iPatLen)    {        if (MainStr[i] == PatternStr[j])        {            ++i;   //当前字符匹配成功,两者均往后走一个位置            ++j;        }        else        {            i = i - j + 1;  //每次未匹配成功时i回退到原来i的后一位置            j = 0;          //模式串每次未匹配成功时回退到0位置        }    }    if (j >= iPatLen) //条件满足说明j走过了模式串的所有字符    {        return i - j;      }    return -1;}int main(void){    const char *MainStr = "abcdabddabc";    const char *PatternStr = "dabc";    int iPos = BF(MainStr, PatternStr);    printf("iPos = %d\n", iPos);    return 0;}

二、KMP算法

KMP算法的高明之处在于当主串和模式串在某个字符不匹配时,指示主串匹配位置的变量不需要回退,而直接回退指示模式串匹配位置的变量,而且该变量回退时也不需要像BF算法中回退到起始位置,而是基于原来已匹配过的结果来回退。关于KMP算法推导的过程很多博客中都有,而且很多书上也讲的很清楚,大家可以参考严奶奶的《数据结构》,里面有很清楚的推导,我就不写推导过程了。下面就我在理解KMP算法中遇到的问题加以说明,并给出KMP算法的实现代码。
问题1.next数组的本质
找到匹配成功部分的两个尽可能长的相等的真子串,一个子串以0下标开始,另一个真子串以j-1下标结尾。(这里的j表示模式串的第j个位置,从0开始计数)
问题2.next[j]=k的含义
next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀
问题3.解释next[0]=-1,next[1]=0
假设i指示主串的当前匹配位置,j指示模式串的当前匹配位置。从问题2可以知道next[0]=-1的含义就是当模式串和主串的第一个字符不匹配时,应该回退到-1这个位置,但是我们的数组下标最小为0,此时已经越界了。但是我们知道下一次匹配的情况,那就是主串的匹配位置往后走一个(即i+1位置),继续和模式串的0号位置匹配,所以此时应该将模式串的匹配位置也向后走一个(即从-1加1为0,从0号位置开始匹配,便于写代码)。同理next[1]=0,这个更容易理解,它表示的含义是当模式串的1号位置和主串不匹配时,模式串应该回退到0号位置进行匹配,这是理所当然的做法,也是退到无路可退。
问题4.解释if(j==-1 || sub[j] ==s[i])中j==-1这个条件
当j为-1时,说明此时模式串的第一个字符和主串不匹配,此时模式串第一个字符应该和主串的下一个字符继续比较,即应该要sub[0]和s[i+1]比较,这两种状态切换也是i++和j++的一个过程,所以可以将两者合在一起来写。

代码实现

#include<stdio.h>#include <string.h>#include <stdlib.h>void GetNext(const char *PatternStr, int *next){    int i = 1;    int j = 0;    next[0] = -1;    next[1] = 0;    int iPatLen = strlen(PatternStr);    while (i < iPatLen)    {        if (j == -1 || PatternStr[i] == PatternStr[j])        {            ++i;            ++j;            next[i] = j;        }        else        {            j = next[j];        }    }}int KMP(const char *MainStr, const char *PatternStr){    int i = 0;    int j = 0;    int iMainLen = strlen(MainStr);     //主串长度    int iPatLen = strlen(PatternStr);   //模式串长度    int *next = (int*)malloc(sizeof(int) * iPatLen);    GetNext(PatternStr, next);    while (i < iMainLen && j < iPatLen)    {        if (j == -1 || MainStr[i] == PatternStr[j])        {            ++i;            ++j;        }        else        {            j = next[j];        }    }    if (j >= iPatLen)    {        return i - j;    }    return -1;}int main(void){    const char *MainStr = "abcdabddabc";    const char *PatternStr = "dabc";    int iPos = KMP(MainStr, PatternStr);    printf("iPos = %d\n", iPos);    return 0;}
在最后给出一点提醒,大家仔细看KMP()和GetNext()这两个函数,可以发现其实两者很相似,想想为什么,可以更好帮助你理解KMP算法。
阅读全文
0 0
原创粉丝点击