BF算法和KMP算法实现

一.BF算法

暴力匹配(BF)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较,直到得出最后的匹配结果。

我们来看看BF的代码实现:
以一组字符串(S=“abcdefg”,T=”cde”)为例:

int BF(const char *str,const char *sub,int pos)
{
    assert(str!=NULL&&sub!=NULL);
    int i=pos;//用i来记录S中字符的位置
    int j=0;//用j来记录T中字符的位置
    int lens=strlen(str);
    int lensub=strlen(sub);
    //当S不为空并且T不为空时,逐个进行字符的比较
    while(j<lensub&&i<lens)
    {
        if(str[i]==sub[i]) //当S中第i个字符与T中第j个字符相等时,i向后移一个,j向后移一个 {
            i++;
            j++;
        }
        else 
        //当S中第i个字符与T中第i个字符不相等时,i回到前一个位置的下一个位置,j回到0号位置
        {
            i=i-j+1;
            j=0;
        }

    }
    if(j>=lensub)
    //当j走完T的长度时,也就说明T在S中匹配成功
    {
        return i-j;
        //此时返回字串在主串中的下标位置,此时i在该返回下标位置+j的长度位置,所以返回i-j,本例中返回下标3
    }
    //匹配失败,则返回-1,因为-1下标不存在,不能返回0下标
    else return -1;
}

关于BF算法:
1.当第一个字符不相同时j也会继续向后比较,比如例子中的“abcdefg”和“def”,当“a”和“d”不相同时,则明显之后的两个字符及时相等也不是相同的子串。
2.每次j下标都要回到0号下标,当主串和字串匹配失败时,主串进行回溯会影响效率,回溯之后,主串与字串有些部分比较是没有必要的
综上:
这种简单的丢弃前面的匹配信息的算法,造成了极大的浪费和底下的匹配效率

二.KMP算法

在KMP算法中,对于每一个模式串都会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数,这样就很好的解决了BF算法的缺陷
比如,当匹配失败后,最好是能够将模式字串尽量的右移和主串进行匹配,右移的距离在KMP算法中是这样计算的:在已经匹配的字串中,找到最长的相同的前缀和后缀,然后移动使他们重叠,这个位置就是j要回退的位置,这样j就不用每一次都回到0号位置了,每一次j回退的位置存储在一个数组里,称之为next数组
那么j回退的位置什么是最合适的呢
我们通过一组图来看一下:
《BF算法和KMP算法实现》
根据以上理论,我们求得模式串的next数组:
《BF算法和KMP算法实现》

以下是代码实现:

void GetNext(int *next,const char *sub)
{
    next[0] = -1;
    next[1] = 0;
    int lensub = strlen(sub);
    int i = 2;//当前的i
    int k = 0;//前一项的K值
    while(i < lensub)
    {
        if(k == -1 || sub[i-1] == sub[k]) {
            next[i] = k+1;
            i++;
            k = k+1;
        }
        else
        {
            k  = next[k];
        }
    }
}
int Kmp(const char *str,const char *sub,int pos)
{
    int i = pos;
    int j = 0;
    int lens = strlen(str);
    int lensub = strlen(sub);
    int *next = (int *)malloc(sizeof(int) * lensub);
    assert(next != NULL);
    GetNext(next,sub);
    while(j < lensub && i < lens)
    {
        if(j == -1 || str[i] == sub[j]) {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
    }
    if(j >= lensub)
    {
        return i-j;
    }
    else
    {
        return -1;
    }
}

那么KMP算法可不可以优化呢?我们来看看以下例子:
以字符串”a b a b c”为例,当第二个a失配后,说明被匹配字符串一定不为a,这时候我们可以将当前值与串的sub[next[i]]值进行比较,如果相同,则将nextval[next[i]]值存入nextval[i],不同则将当前值的next的值存入nextval[i];
以一个模式串为例,求出他的next值和nextval值:
《BF算法和KMP算法实现》
接下来就是代码实现:
(在next数组的基础上,我们实现KMP的优化)

void NextVal(int *nextval,const char *sub)
{
    nextval[0]=-1;
    nextval[1]=0;
    int lensub=strlen(sub);
    int i=2;
    int k=0;
    int *next = (int *)malloc(sizeof(int) * lensub);
    assert(next != NULL);
    GetNext(next,sub);
    while(i < lensub)
    {
        if(k == -1 || sub[i] == sub[next[i]]) {
            nextval[i]=nextval[next[i]];
            i++;
            k=k+1;
        }
        else
        {
            k  = next[i];
        }
    }
}

三.时间复杂度

从BF算法到KMP算法的优化,更优化的就是为了实现更优化的代码,我们来看看这些排序的时间复杂度:
《BF算法和KMP算法实现》

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