KMP算法-如何理解Next数组的计算

  最近看了些KMP算法的讲解,但由于个人理解能力有限,所以感觉不是很好理解,最后在弄懂之后,决定把它按自己的理解记录下来,希望能对想要学习的朋友有所帮助

此处套用百科上对KMP算法的解释:

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)

 

根据上文所述,很明显,该算法的核心是利用已经匹配过的数据,避免后面无谓的冗余匹配,它的关键要点就是Next数组

Next数组大约就是下面这种东西:

模式串ABABCA
下标012345
Next值001201

Next值按个人理解就是指模式串中重合的公共前后缀中最大的重合长度,也就是最大公共前后缀

首先,这里需要说明一下前后缀的含义:

前缀:不包含最后一个字符,以首字符开头的连续子串为前缀

后缀:不包含第一个字符,到最后一个字符结束的连续子串为后缀

 

那么模式串ABABCA是如何求出对应的Next值呢,我们可以将其分解进行计算


前缀后缀
AA
ABCA
ABABCA
ABABABCA
ABABCBABCA

知道了前后缀,计算两者重合的长度就很简单了,我们对模式串进行推导一下:

A                     

                       只有一个字符,无法对比,将其置空为0

AB                 

                       前缀A,后缀B

                       没有重合串,将其置空为0

ABA               

                       前缀A,AB,后缀A,BA

                       重合串为A,长度为1

ABAB             

                        前缀A,AB,ABA,后缀B,AB,BAB

                        重合串为AB,长度为2,将其置为2

ABABC           

                        前缀A,AB,ABA,ABAB,后缀C,BC,ABC,BABC

                        均无法与后缀带C的后缀重合,将其置为0

ABABCA          前缀A,AB,ABA,ABAB,ABABC,后缀A,CA,BCA,ABCA,BABCA

                         重合串为A,将其置为1

最终我们得出了和上面一样的Next数组

模式串ABABCA
下标012345
Next值001201

或许看完上面你会疑惑这个最大公共前后缀,最大是什么意思,上面没有体现出来,我们以AABAA为例:

AABAA

                        前缀A,AA,AAB,AABA,后缀A,AA,BAA,ABAA

                        其中前后缀A,AA均重合,这个时候,AA即为最大的前后缀,长度为2


Next数组的计算逻辑我们已经知道了,那么该如何转化为代码呢,这里才是最重要的关键点,好吧,这里想不出很好的文字层面的解释,建议看视频辅助,更加的形象,这里直接贴代码好了

/// <summary>
/// 计算模式串的Next(Prefix)数组值
/// </summary>
/// <param name="pattern">模式串</param>
/// <returns></returns>
public static int[] Prefix_Table(string pattern)
{
    //根据Pattern模式串,计算获取相对应的Next(或Prefix)数组

    int len = 0, i = 1;
    int[] prefix = new int[pattern.Length];

    prefix[len] = 0;//首元素只有一个无法计算前后缀匹配情况,直接赋值0
    while (i < pattern.Length)
    {
        if (pattern[len] == pattern[i])//如果匹配成功
        {
            len++;              //前缀串长度+1
            prefix[i] = len;    //赋值当前位置匹配最大前后缀长度
            i++;                //后缀索引加1
        }
        else//
        {
            if (len > 0)//如果之前匹配成功过,那么回溯到上一次的匹配长度进行匹配
            {
                len = prefix[len - 1];//前缀指针位置回溯上一次匹配位置
            }
            else//前面没有匹配成功过或当前匹配回溯无任何匹配成功状况
            {
                prefix[i] = len;
                i++;
            }
        }
    }
    return prefix;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值