【超详细】kmp算法,直击本质

最近在学kmp算法,奈何研究了2天,才搞懂,很多人并没有理解kmp算法的本质,所以我尽量在这里把这个算法的原理说清楚。

首先假设有一个text数组

abxabcabcaba

和pattern数组

abcaba

在不会kmp算法的时候我们只能选择bf方法,时间复杂度为O(M*N),效率非常低,那么为什么效率会这么低呢?相必你也非常清楚,bf方法回溯太多了,时间复杂度肯定会非常高,所以想解决这个问题,我们就要解决回溯的问题,所以便有了kmp算法,在接下来的学习中,你会感受到这个算法的巧妙之处。

首先我们需要弄懂一个概念,前缀表(不包括最后一个)和后缀表(不包括第一个),以patter数组为例子,它的前缀有a ab abc abca abcab,他的后缀有b bc bca bcab bcaba,知道这个概念以后,我们来求一下pattern数组最长相等的前后缀,首先第一个默认是0,所以咱们a下面写0,ab也没有写0,abc没有写0,abca有一个相同所以写1,依此类推···

表示如下:

abcaba
000121

这个概念弄懂以后,我们该如何实现呢?这一步非常的巧妙,没有弄懂的话就多举几个例子跟着走,我们需要一个循环来计算最长相等前后缀,创建一个prefix数组,然后定义len=0和i=1,如果他们不相等并且len=0,那么下面的数组写0,然后i继续往下走,如果相等len加一(这里是在原来的基础上加一),把len的值赋给下面数组的第i个,i继续往后走,不过这里有一个情况,就是如果突然有一个中断了该怎么办?很多人会从头开始比较,我个人认为是可以得出正确答案的(如果不是希望指正),但是这里选择从斜下方回溯会更好,因为它prefix数组保存着最大相等前后缀,我们并不需要从头开始比较,而是从最大前后缀的下一个比较会更好(自己的理解)

这里可以换别数值去思考,慢慢体会其中的奥秘, 下面两个数组进行比较时你会更容易理解这里为什么要这么做,这里的代码如下:

void prefix_table(char pattern[],int prefix[],int n)
{
    prefix[0]=0;
    int len=0;
    int i=1;
    while(i<n)
    {
        if(pattern[i]==pattern[len])
        {
            len++;
            prefix[i]=len;
            i++;
        }
        else
        {
            if(len>0)
            {
            len=prefix[len-1];
            }
            //判断永远不相等
            else
            {
            prefix[i]=0;
            i++;
            }
            
        }
    }
}

既然咱们是斜着来用的话,不妨把第一位设置为-1(默认),整个prefix数组往后移,这样用起来很方便,最后一位是不需要的,为什么呢?因为既然是斜着匹配了,永远也用不到最后一个,所以咱们的数组可以理解为这样:

abcaba
-100012

这个代码对于我们来说非常简单,如下:

void move_prefix_table(int prefix[],int n)
{
    int i;
    for(i=n-1;i>0;i--)
    {
        prefix[i]=prefix[i-1];
    }
    prefix[i]=-1;
}

我们做了这么多准备工作是为了干什么?没错就是为了减少回溯,所以前面才是最重点的步骤,也同样是很多人难以理解的部分,接下来的工作就非常简单了,只需要几个判断就可以。

 

 

这样思路已经很清楚了吧,代码如下:

void kmp_serach(char text[],char pattern[])
{
    //计算pattern的大小
    int n=strlen(pattern);
    int m=strlen(text);
    int* prefix=(int*)malloc(sizeof(int)*n);
    prefix_table(pattern,prefix,n);
    move_prefix_table(prefix,n);
    //text[i]      len[text]   =m
    //pattern[j]   len[pattern]=n
    int i=0;
    int j=0;
    while(i<m)
    {
        if(j==n-1&&text[i]==pattern[j])
        {
            //i-j就是位置
            printf("found pattern at %d\n",i-j); 
            //匹配完成后继续匹配
            j=prefix[j];
        }
        if(text[i]==pattern[j])
        {
            i++;
            j++;
        }
        else
        {
            j=prefix[j];
            if(j==-1)
            {
                j++;
                i++;
            }
        }
    }
}

 借鉴视频:KMP字符串匹配算法2_哔哩哔哩_bilibili

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值