KMP算法通俗易懂(不懂你打我)

KMP算法

分析Brute-Force算法的缺点(简称暴力算法)

先举个例子,看看暴力算法的执行过程

Snipaste_2022-05-13_16-25-16


该算法的缺点从第五步开始显示出来,分析如下

Snipaste_2022-05-13_16-28-19

第五步进行了指针回退,重新开始模式串和目标串的比较,这有什么不好呢?实际上,第五步中目标串的第二个字符在第二步已经与a比较过。

重复比较如下图所示

Snipaste_2022-05-13_16-33-26

Snipaste_2022-05-13_16-33-42

这是暴力算法的缺点,进行多次重复的比较

有没有什么办法可以跳过多次重复比较呢?

KMP算法的思想

我们尝试去跳步

举一个暴力算法失败匹配的例子,如下图所示

Snipaste_2022-05-13_16-45-18

o和a不匹配,前面的字符hell比对成功。这意味着hell两个串是一模一样的,我们能不能这样想,既然都一样了,然后o和a又比较失败了。我们能不能直接跳过hell的匹配(因为已经比对过了),直接就从失败匹配的下一个地方开始?

如下图

Snipaste_2022-05-13_16-47-16

回到第一个例子,刚才的跳步有个问题

Snipaste_2022-05-13_16-51-54

是不是发现跳太多了?多在哪里?那我们跳步应该跳到什么地方比较合适?再举个例子给你。

Snipaste_2022-05-13_17-04-34

OK,观察上面的例子,我们发现,我们只要把失配位置前面的部分分析分析,就能发现,只要把模式串的头头和目标串的屁屁一样的地方对上,就是合适的。

诶?所以问题就归结成,找模式串的最大相等前后缀,为什么要最大呢?因为想尽可能跳步。

最大相等前后缀

next数组概念介绍

以上一个例子求最大相等前后缀,假设我们在j处发生失配,则有最大相等前后缀next[j]

Snipaste_2022-05-13_17-16-10

此处,next[4]=2,这代表这在坐标4之前,最大相等前后缀为2。

仔细分析发现,最大相等前后缀跟目标串没关系,跟模式串最大相等前后缀有关系。

next数组求解例子

再举一个例子aaab,求在不同位置失配的next数组

在0处就失配,意思就是第一个就失配,那都不用讨论前后缀了,指针直接指向下一处。我们令这种情况为-1,方便算法的实现
Snipaste_2022-05-13_17-27-14

在1处失配,最小相等前后缀为0,没有。我们把这种情况列为其他情况,
Snipaste_2022-05-13_17-28-00

在2处失配,最小相等前后缀为1,
Snipaste_2022-05-13_17-33-50

这里需要注意啊,最小相等前后缀的后缀不能从0开始,不然上下是不是就一样了,还能跳步吗?
Snipaste_2022-05-13_17-35-41

在3处失配,最小相等前后缀为2
Snipaste_2022-05-13_17-37-20

我们可以整理成下表

Snipaste_2022-05-13_17-32-49

next数组公式如下

Snipaste_2022-05-13_17-47-39

算法实现如下

Snipaste_2022-05-13_18-06-39

求next数组算法

//基本思路就是从最大的可能的前后缀开始,慢慢缩小范围
void GetNext(SqString t,int next[])//有模式串t求出next数组
{
    int j=0,k=-1;
    next[0]=-1;
    while(j<t.length-1)
    {
        if(k==-1 || t.data[j]==t.data[k])
        {
            j++;k++;
        }
        else k=next[k];
    }
}

KMP算法实现


int KMPIndex(SqString s,SqString t)
{
    int next[MaxSize],i=0,j=0;
    GetNext(t,next);
    while(i<s.length&&j<t.length)
    {
        if(j==-1||s.data[i]==t.data[j])
        {
            i++;
            j++;
        }
        else
            j=next[j];
        if(j>=t.length)
            return(i-t.length);
        else
            return(-1);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李南想做条咸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值