字符串匹配:BF算法,KMP算法

一、BF 算法:
朴素匹配(暴力匹配)
主要思想:
从指定pos位置开始匹配,主串指针i初始为pos、子串指针j 初始为0;

1、开始匹配:主串、子串分别指向的当前元素进行比较,
相同,则i、j 同时后移一位
不同,则i 会退至开始位置的后一位、j 回退至0(子串首元素);

2、继续开始下一次匹配;

3、结束判断:
当子串j 后移至尾部,则匹配成功
当主串i 后移至尾部,且子串j 没有移至尾部,则匹配失败。

int BF(Str *s,Str *sub,int pos)//朴素匹配
{
    assert(s != NULL && sub != NULL);
    int i = 0;
    int j = 0;
    if(pos < 0 || pos > s->length || pos + sub->length > s->length) {
        return -1;
    }//判断pos位置是否有效
    //开始匹配
    while(i < s->length && j < sub->length) {
        if(s->elem[i] = sub->elem[j]) {
            i++;
            j++;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    //判断是否完全匹配:sub 能遍历结束则完全匹配 if( j = sub->length) {
        return i - j;
    }
    else
    {
        return -1;
    }
}

二、KMP算法:
主要思想:
核心在于next数组 : next[j] = k(保存j 需要回退的位置)
(即当匹配失败时,主串i 不用回退,只需要当前的j 回退至next数组中保存的 k 值)。

k 值计算规则:
1、next[0] = -1 (一开始就匹配失败),next[1] = 0 (仅0号下标匹配成功);

2、在子串j 指向当前元素之前的部分子串中判断下标以0开头的真子串 == 下标以j-1结尾的真子串 是否存在,
存在,则当前的next[j] = 真子串的长度
不存在 , 则当前的next[j] = 0;

依据主串s,子串p:
假定p0…pk-1 = px…pj-1,
那么 k-1 = j-1-x , x = j-k,
则 p0…pk-1 = pj-k…pj-1

static void GetNext(int *next,Str *sub)//next数组
{
    next[0] = -1;
    next[1] = 0;
    int i = 2;//当前的i
    int k = 0;//前一项的K值
    while(i < sub->length) {
        if(k == -1 || sub->elem[i-1] == sub->elem[k]) {
            next[i] = k+1;
            i++;
            k = k+1;//k++;
        }//没有真子串时赋0 / 有真子串时保存其长度至数组,并用k 保存其当前长度(若i++后仍匹配则直接在当前k基础上+1else
        {
            k  = next[k];
        }//(反之,若i++后不匹配则将k 归零,重新计算真子串长度)
    }
}

int Kmp(Str *s,Str *sub,int pos)
{
    if(pos < 0 || pos > s->length || pos + sub->length > s->length) {
        return -1;
    }//判断pos位置是否有效
    int i = pos;
    int j = 0;

    int *next = (int *)malloc(sizeof(int) * sub->length);
    assert(next != NULL);
    GetNext(next,sub);//得到next数组,用于定位匹配失败时j 回退的位置
    //开始匹配
    while(j < sub->length && i < s->length) {
        if(j == -1 || s->elem[i] == sub->elem[j]) {
            i++;
            j++;
        }//匹配成功
        else
        {
            j = next[j];
        }//匹配失败,j 需要回退至k
    }
    //判断是否完全匹配
    if(j >= sub->length) {
        return i-j;
    }
    else
    {
        return -1;
    }
}

三、对于next数组的优化:
nextval数组(对于回退后认为与回退前字符相同的情况下,则直接将其回退至首次回退的地方,进而可以减少一部分多余的字符匹配比较)

四、BF算法与KMP算法的比较:
m代表主串长度,n代表子串长度,
1、BF算法:
时间复杂度为 O(mn)
算法思想:匹配失败时,主串 i、子串 j 均回退,且 j 每次都回退至子串首元素位置

2、KMP算法:
时间复杂度为 O(m+n)
算法思想:匹配失败时,主串 i 不回退、仅子串 j 回退,且 j 只需回退至 k

因此,KMP算法比BF算法尤为高效。

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