KMP算法学习笔记


KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。(百度的嘻嘻)

KMP难在对于next数组的构建与理解

next[i]的定义是 到 i 处已找到的最长相同前后缀的长度

next的作用是当字符串s1和字符串s2匹配到s1[i]和s2[j]时, 若s1[i]和s2[j]不相等,就不必再把j重新从0开始找,而是跳到和当前指针j有相同前缀的地方。

s1[i]和s2[j]不相等,但之前有一部分是相同的

举个例子:比如说 ababc 和 ababd, 比较到c和d时发生了冲突,但这时发现d的前面有一个ab, c的前面也有一个ab,这时候我们只要跳到前一个ab处就可以继续比较

ababc ababd 中的 c和a了, 省了不少时间


首先我们要理解一波前缀和后缀, 前缀和后缀指的是一个集合

例如  ababa

其前缀为{a,ab,aba,abab}(不包括最后一个)

后缀为{a,ba,aba,baba}(不包括第一个)

可以很清楚的看到,最长前后缀就是 aba, 长度为3

我个人理解的next[i]是跳到与i的后缀 有最长的相同前缀的地方

贴一份next数组构建的代码, 也就是s2的自我匹配


for(i = 1; i < bn; i++){
		while(j && s2[j]!=s2[i]) j = next[j-1];//指针跳到j-1的最长前缀处
		//s2[j]!=s2[i],但是既然j>0,说明从s2[0]到s2[j-1]==s2[i-j]到s2[i-1],这时跳到新的j,新的s[j-1]也会等于原来的s[j]
		if(s2[j]==s2[i]){
			next[i] = ++j;//next[i] = 当前指针(因为字符串是从0开始的,而指针到j最长前缀为j-1)+1, 同时 j往下移一个指针
		} 
	}
然后贴出完整代码:
#include <cstdio>
#include <cstring>
using namespace std;
int next[1001];
char s1[1000001], s2[1001];
//next[i]指 到 i 处的最长前后缀 
int main(){
    int i, j = 0, an, bn;
    scanf("%s%s", s1, s2);
    an = strlen(s1); bn = strlen(s2);
    for(i = 1; i < bn; i++){
        while(j && s2[j]!=s2[i]) j = next[j-1];//指针跳到j-1的最长前缀处
        //s2[j]!=s2[i],但是既然j>0,说明从s2[1]到s2[j-1]==s2[某个位置开始]到s2[i-1],这时跳到新的j,新的s[j-1]也会等于原来的s[j]
        if(s2[j]==s2[i]){
            next[i] = ++j;//next[i] = 当前指针(因为字符串是从0开始的,而指针到j最长前缀为j-1)+1, 同时 j往下移一个指针
        } 
    }
    j = 0;
    for(i = 0; i < an; i++){
        while(j  && s1[i] != s2[j]){
            j = next[j-1];//如果目前的s1[i]和s2[j]不相等就一直跳到最长前缀处, 直到相等就接着查找 
        }
        if(s1[i] == s2[j]) j++;//相等的话就移动s2[]的指针 
        if(j == bn){
            printf("%d\n", i-j+2);//在第几个字母找到的 
            j = next[j-1];//跳到上一个位置的next处,这样可以下次直接比较s1[i]和s2[j] 
        }
    
    }
    for(i = 0; i < bn; i++) printf("%d ", next[i]);
    return 0;
}
可以去B站搜索KMP算法,印度小哥讲的超级详细
链接:https://www.bilibili.com/video/av3246487/?from=search&seid=17380554719454557667

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值