KMP算法 —— 对Next数组与Nextval数组原理的简单梳理

KMP算法是一种高效的字符串匹配方法,通过构建Next数组减少不必要的匹配。基础KMP算法利用匹配失败后的信息避免重复比较。Nextval数组是其改进版,考虑了模式串匹配失败位置的信息,进一步优化匹配过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

KMP算法的简单梳理

KMP算法是字符串匹配问题的一种效率解法,有很多初学者对KMP的实现一头雾水,以下是我对KMP算法及其改进的简单整理。

基础KMP

KMP算法的核心在于利用匹配失败后的信息,尽量减少模式串与主串的匹配次数而达到快速匹配的目的

其时间复杂度为O(m+n)O(m+n)O(m+n)

假定:主串为S1S2...SnS_1S_2...S_nS1S2...Sn,模式串为P1P2...PmP_1P_2...P_mP1P2...Pm。(模式串为需要匹配的字符串,是主串的子串)

每个元素拥有一个独立的Next()函数用于判断遇到不同的元素该从哪里开始匹配。本质是用于寻找模式串的最长相同前缀和后缀。

举个例子,模式串 str=abaabcac,假设主串有一段为abab,前三个字符aba与模式串前三个完美匹配,但是第四个字符出现了差错,Next数组中存储了模式串在匹配失败位置前的公共前缀信息,前三个字符之中,str[3]与str[1]相同,构成了公共前缀,因此第四个位置出错之时,我们可以跳过str[1],直接将第四个位置的字符与str[2]的字符进行匹配。

比较时只移动模式串,找到当前已匹配的串与模式串的最长前缀

以下是Next数组的计算公式:

Next[i]={0(j=1时)Max(k ∣ 1<k<j and p1p2...pk−1=pj−k+1...pj−11(其他情况)Next[ i ]=\begin{cases} 0 (j=1时)\\Max(k\ |\ 1<k<j \ and\ p_1p_2...p_{k-1}=p_{j-k+1}...p_{j-1}\\1 (其他情况)\end{cases}Next[i]=0j=1时)Max(k  1<k<j and p1p2...pk1=pjk+1...pj11(其他情况)

模式串为abaabcac时得出的Next数组:

j12345678
String[j]abaabcac
Next(j)01122312

Next数组计算代码:

void Make_next(String p, int *pNext) 
{
    int i, k;
    i = 1;
    k = 0;
    int j = 1;
    pNext[1] = 0;
    while(i < p.size())
    {
        if(k == 0 || p[i] == p[k])
        {
            j++;
            k++;
            pNext[j] = k;
        }
        else k = pNext[k];
    }
}

改进后的KMP

基础的KMP算法并没有运用到匹配失败的信息,即P[J](模式串匹配失败位置)的信息,在使用Next[j]进行回溯时进行了多余的运算。

就比如说,主串为abacababc,模式串为abab,在进行匹配时,在第四个位置abac与abab匹配失败,按照原有的KMP算法,主串的c将与第二个字符重新进行匹配,但是显而易见,在匹配第四个位置时字符不为 b,其对第二个字符 b 的匹配也必然失败。

所以说KMP的改进就是增加了对模式串匹配失败位置的信息处理。

因此,在需要子串指针回溯时,进行当前位置元素与回溯之后的位置元素比较,如果相等,子串的指针继续回溯

以下是改进的 Next 数组:Nextval 数组的推导公式:

Nextval[i]={next[i] (pi != pnext[i])nextval[next[i]] (pi == pnext[i])Nextval[ i ]=\begin{cases} next[i] \ (p_i\ !=\ p_{next[i]})\\nextval[next[i]]\ (p_i\ ==\ p_{next[i]})\end{cases}Nextval[i]={next[i] (pi != pnext[i])nextval[next[i]] (pi == pnext[i])

j12345
模式串aaaab
next[j]01234
nextval[j]00004

以下是计算Nextval数组的代码:

void get_nextval(string p, int* nextval) 
{
    int i = 1, j = 0;
    nextval[1] = 0;
    while(i < p[0])
    {
        if(j == 0 || p[i] == p[j])
        {
            i++;
            j++;
            if(p[i] != p[j])
            {
                nextval[i] = j;
            }
            else
            {
                nextval[i] = nextval[j];
            }
        }
        else j = nextval[j];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值