KMP 模式匹配算法

        KMP 模式匹配算法是为了解决在一个主串中去寻找子串的问题,在没有 KMP 算法之前,人们采用那种人们最容易想到的以字串按照主串从首位开始一位一位后移的方法,但是这种方法很费时间,例如下面例子:

 

        当比较到最后一位的时候,发现该段不匹配,接下来指针又要回溯到开头,开始第二个遍历,但是很明显第二个也不会存在匹配的情况,但是这种算法是无法判别的,所以就需要一种非常高效的匹配算法,于是,就衍生出了 KMP 算法,要熟悉 KMP 算法,就必须要知道模式串的 next 数组怎么推导。

next 数组的推导  

以模式串 abcabx 为例:

        因为在一般数据结构中,在对 next 数组推导时,数组下标都是从 1 开始,而 next[0]一般保存模式串的元素个数。next 数组值推导公式是

 值 = 前后缀最大字符匹配数 + 1

步骤是:
        1.当 j 为 1 时,因为 j = 1 指向的模式串位置前面没有字符,所以默认为 0
        2.当 j 为 2 时,因为 j = 2 指向的模式串位置前面只有一个字符 a,前后缀匹配数为 0,值为 1
        3.当 j 为 3 时,因为 j = 3 指向的模式串位置前面有 ab,前后缀匹配数为 0,值为 1
        4.当 j 为 4 时,因为 j = 4 指向的模式串位置前面有 abc,前后缀匹配数为 0,值为 1
        5.当 j 为 5 时,因为 j = 5 指向的模式串位置前面有 abca,前缀 a 和后缀 a 匹配,前后缀匹配数为 1,值为 2
        6.当 j 为 6 时,因为 j = 6 指向的模式串位置前面有 abcab,前缀 ab 和后缀 ab 匹配,前后缀匹配数为 2,值为 3
程序求取 next 数组的示例:
//T 为模式串
void get_next(char *T,int *next)
{
    int i,j;
    i = 1;//用来遍历模式串
    j = 0;//用来回溯
    next[0] = strlen(T);
    next[1] = 0;
    while (i < next[0])
    {
        if(j == 0 || T[i-1] == T[j-1])
        {
            ++i;
            ++j;
            next[i] = j;
        }
        else
        {
            j = next[j]; /*若字符不协调,则 j 值回溯*/
        }
    }
}

以 next 数组进行 KMP 匹配推导算法程序

/**
* @brief KMP 模式匹配算法
* 
* @param S 主串
* @param T 字串
* @param pos 从主串的第几个字符开始匹配
* @return int 成功返回匹配的位置,失败返回-1
*/
int Index_KMP(char *S,char *T,int pos)
{ 
    int i = pos; //主串下标
    int j = 0; //子串下标
    int next[255]; //next 数组
    get_next(T,next); //计算 next 数组
    while (i < sizeof(S) && j < sizeof(T))
    {
        if(j == 0 || S[i] == T(j))
        {
            if(j != 0 || S[i] == T(j))
            j++;
            i++; 
        }
        else
        {
            j = next[j+1]; //j 值回溯,因为 next[0]保存的是模式串长度,所以 j+1;
        }
    }
    if(j == sizeof(T))
        return i - sizeof(T[0]);
    else
        return -1; 
}

KMP 改进算法的 nextval 数组推导

        因为上面通过 next 数组的方式去实现 KMP 算法在一些情况下还是存在缺陷的,例如一个主串为 aaaabcde,模式串为 aaaaax,模式串的 next 数组为 012345,那么匹配的算法步骤就是

         当 i 和 j 都为 5 时发现不匹配,这个时候 j 回溯,next[5] = 4;,回溯到 4,然后因为上一 次 a 就于 b 不匹配,回溯到 4 还是 a,自然是还不匹配,按照人为理解来说,模式串因为存在很大相同的,不需要一步一步回溯,直接 i 后移 j 回溯向后比较就行了,也就是上图的 2345 步骤是多余的,那么要解决这种问题,就需要进一步对 next 数组进行升级,形成 nextval 数组,下面将给出 nextval 数组的推导程序示例:

//T 为模式串
void get_next(char *T,int *nextval)
{
    int i,j;
    i = 1;//用来遍历模式串
    j = 0;//用来回溯
    nextval[0] = strlen(T);
    nextval[1] = 0;
    while (i < next[0])
    {
        if(j == 0 || T[i-1] == T[j-1])
        {
            ++i;
            ++j;
            if(T[i-1] != T[j-1]) //如果单纯只是因为 j == 0 进来的
                nextval[i] = j;
            else
                nextval[i] = nextval[j];
        }
        else
        {
            j = next[j]; /*若字符不相同,则 j 值回溯*/
        }
    }
}

 

         KMP 的改进算法就是改进了 next 数组,优化了其中的回溯值,其他的算法匹配步骤依然不变。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值