KMP算法(多种实现方式)

KMP算法核心思想利用已经匹配的数据,去除无效的从头匹配KMP算法流程首先我们找到 i=9,j=9时不匹配,如果时暴力算法,此时i应重新来到i=2的位置,j返回j=1的位置,开始新一轮的匹配这样暴力匹配,就白白浪费了已经匹配的串,那么问题来了,我们应该如何利用已经匹配的串呢??我们看着图片,假设i返回i=1,j返回j=1,当i++,i指向b,j++,j指向a,此时就不匹配了,又要重新开始,i来到3,j又回到j=1,双方指向第一个元素就不匹配,kmp算法的核心就是过滤掉这种低效的匹配,我
摘要由CSDN通过智能技术生成

KMP算法核心思想

利用已经匹配的数据,去除无效的从头匹配

KMP算法流程

首先我们找到 i=9,j=9时不匹配,如果时暴力算法,此时i应重新来到i=2的位置,j返回j=1的位置,开始新一轮的匹配
在这里插入图片描述
这样暴力匹配,就白白浪费了已经匹配的串,那么问题来了,我们应该如何利用已经匹配的串呢??

我们看着图片,假设i返回i=2,j返回j=1,i++,j++,i指向b,j指向a,此时就不匹配了,又要重新开始,i来到3,j又回到j=1,双方指向第一个元素就不匹配,kmp算法的核心就是过滤掉这种低效的匹配,我们往下看

在这里插入图片描述

我们直到i=9和j=9前面的串 a a b a a b a a无论在S串或者J串是完全相等的,这也就意味着,我下面的操作无论在S串操作或者在J串操作,只要不出这个范围,在哪里操作都是一样的,因为他们都是同一串!

我们找到紧挨着i=9,元素b前的一串后缀(aabaa),一定要紧挨着,然后找到 J串从第一个元素开始的,与aabaa相等的前缀! 为什么要这样找呢?? 我们可以发现i从这个前缀的第一个元素开始能够完全匹配,J从第一个元素开始一直到这个缀结束,这样就可以过滤掉,大量的无效匹配,比如刚才的 i=2,i=3,都属于无效匹配
在这里插入图片描述
此时有个疑问,既然是紧挨着i=9的后缀,和J第一个元素出发的前缀,那么aa不可以吗?? 当然可以啦,从aa开始匹配,也就是i=7开始,新一轮匹配,但是这样会丢解,也就是,i跳的步子大了,跨过了可行解的下标!

通过上面的铺垫,我们引出一个概念

最长相等前后缀

从第一个元素开始,不包括最后一个元素结束,和从最后一个元素开始,不包括第一元素得出的前缀和后缀,前缀和后缀满足,相等且最长

通过上面讲解,我们就是要利用这个最长相等前后缀,达到过滤无效匹配的效果,上面讲过,我的操作完全可以只在J串进行,无需在S串进行,所以我让i=9不动,让J移动到J=5的位置,开始匹配i,和j+1所对应元素是否相等即可! 这也就相当于暴力算法,从i=4开始匹配,匹配到了i=8的位置,故通过寻找最长相等前后缀可以快速达到此效果!!

故:在使用kmp算法时,需要求出J串的每个位置的最长相等前后缀 (有的算法实现时会求出,每个位置 ‘前’ 的最长相等前后缀,这在后面会讲解,不同的求法,实现代码不同,因此会有差异)

求解Next数组

我们依J串为例a a b a a b a a a a求出它的Next数组(next数组存放每个位置的最长前后缀)
在这里插入图片描述
那么如何用代码实现呢?
在这里插入图片描述
我们先观察next数组,我们发现只要多进来一个元素,并且满足最长前后缀,那么next数组就会增加1,那么如果不满足呢? 实际上我们观察代码可看到i起始从2开始,j从0开始,每次用j+1的位置进行匹配,我们可以理解为虽然用的是同一条串,但是由于i和j从不同起点出发导致,出现了一个新两个串匹配问题,那么就可以转化为我们刚刚讲的kmp思想

我们看以下匹配模拟过程
在这里插入图片描述
当i=9时,j=5,j+1元素为b与a并不匹配,也就意味着i=9无法继续发扬光大,无法继承上依次最大前后缀,所以需要寻找新的,也就回到了最初的两个串匹配问题,不同则回退,所以j回退到next[5]的位置(此时next[5]已经求出来了),j落到j=2,用j+1继续匹配i,发现匹配仍然失败b!=a,故j移动到next[j]位置也就是j=1,继续用j+1匹配,匹配成功,退出while循环,i++,j++,接着算出next[10]

这里解释以下为什么j从0开始,i从2开始,如果j从0开始,i从2开始,那么每次只需要用j+1去匹配i(“进可攻”),如果不匹配直接j=next[j] (“退可守”),所以我们现在的next数组含义是如果正在匹配的元素不相等,则需要他们之前的next[j],而不是他们自身的next[j],后面会讲解,直接利用自身的next不必需要前一个位置的next回退(一定注意区分)

模式串与主串匹配过程

在这里插入图片描述

这里的代码和求next函数代码十分相似,只是求next函数用的一个串,这里变为了两个串,注意起始位置,i从1开始j从0开始,之前是i从2开始,因为只有从2开始才能领先一个位置,用j+1去比较,不然j+1和i指向同一个元素无法达到进可攻退可守的效果!

竞赛版本代码

kmp模板题目洛谷P3375

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;

int ne[maxn];
int la,lb; //主串长la,子串长lb
char a[maxn],b[maxn]; //a主串 b子串

int main()
{
   

    cin>>a+1;
    cin>>b+1;
    la=strlen(a+1);
    lb=strlen(b+1);
    //预处理next函数
    for(int i=2,j=0;i<=lb;i++)
    {
   
        while(j&&b[i]!=b[j+1])j=ne[j]; //如果不匹配每次向前跳
        if(b[i]==b[j+1])j++;//匹配则j指针前移
        ne[i]=j;
    }

    //根据next数组进行匹配
    for(int i=1,j=0;i
  • 21
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
KMP算法中的next数组是用来记录模式串中每个位置之前的最长相同前缀后缀的长度。具体来说,next数组的每个元素next\[i\]表示当模式串的第i+1个字符与文本串不匹配时,模式串应该跳过的位置。\[2\]在KMP算法实现中,通过使用next数组,可以在匹配过程中避免不必要的回溯,提高匹配效率。 next数组的求法有多种方法,其中一种常用的方法是通过动态规划的思想来计算。具体步骤如下: 1. 初始化next数组,将next\[0\]置为-1,next\[1\]置为0。 2. 从模式串的第2个字符开始,依次计算每个位置的next值。 3. 对于位置i,如果模式串的第i个字符与前缀的下一个字符相等,则next\[i\]等于前缀的长度加1。 4. 如果模式串的第i个字符与前缀的下一个字符不相等,则需要根据已知的next值来更新next\[i\]。 - 如果next\[j\]等于-1,或者模式串的第i个字符与前缀的下一个字符相等,则next\[i\]等于j。 - 如果next\[j\]不等于-1,且模式串的第i个字符与前缀的下一个字符不相等,则需要继续向前回溯,即将j更新为next\[j\],然后再进行比较。 - 重复上述步骤,直到找到一个满足条件的j或者回溯到模式串的起始位置。 通过以上步骤,可以得到完整的next数组,用于KMP算法的匹配过程中的跳转操作。\[2\]在KMP算法的代码实现中,可以根据next数组的值来决定模式串的后移位置,从而提高匹配效率。\[3\] #### 引用[.reference_title] - *1* *2* [KMP算法&next数组详解](https://blog.csdn.net/ooblack/article/details/109329361)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [KMP 算法中的 next 数组](https://blog.csdn.net/m0_52423355/article/details/123807325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值