论KMP
一、算法简介
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,
因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。
KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
(博主很懒所以扒了百度词条的简介QAQ
二、算法原理&流程
首先我们对于模式串和主串的暴力匹配,无非是做一次1-n的循环,
对于每个i,都枚举一个1-m,对于A[i-j+1...i]和B[1....j]都必须匹配
当j=m时,则满足了模式串与主串匹配
现在我们考虑,当一次匹配失败后,是否有可以继承的东西,以便于在下一次查找中更加快速呢?
我们发现当A[i-j+1....i+1]与B[1....j+1]不匹配时,可以考虑减小j值,
使得A[i-j+1...i]与B[1....j]保持匹配并继续匹配A[i+1]与B[j+1]
仔细思考后发现,我们需要在B[1...j]中找出最大的k,使得B[1...k]与B[m...m-k+1](后缀)完全相等
于是我们就将模式串做“自我匹配”,使得算法能够更加快速的进行,自我匹配的过程等会说
当我们不停地寻找更小的k,然而一直不能满足条件直到k=0时,我们就不停地i++,直到A[i]=B[1],再重复前面的流程
三、nxt数组(即跳转数组)的求值
这里定义一个数组P,表示当模式串匹配到下标J时,而j+1与A串不匹配,这时最大的k
接下来开始计算数组P
我们对于一个未知的P[j],与许多已知的P[1...j-1],是可以通过P[1....j-1]来得到P[j]的,具体如下:
我们对于一个新的P[j],如果要直接从P[j-1]继承的话,就需要满足B[P[j-1]+1]=B[j]
如果不满足以上条件的话,那么我们考虑将指针k(初始为j-1)移到P[j-1]上,接着开始匹配B[k+1]=B[j],直到满足
如果一直不能匹配的话,那么P[j]=0;如果能够匹配的话,P[j]=P[k]+1;
四、例题(luoguP3375)
地址: https://www.luogu.org/problemnew/show/P3375
#include<bits/stdc++.h> using namespace std; char A[1000010]; char B[1000010]; int n,m,p[1000010]; int j=0; int main(){ scanf("%s%s",A+1,B+1); n=strlen(A+1);m=strlen(B+1); for(int i=2;i<=m;i++){ while(j&&B[j+1]!=B[i]) j=p[j]; if(B[j+1]==B[i]) j++; p[i]=j; } j=0; for(int i=1;i<=n;i++){ while(j&&B[j+1]!=A[i]) j=p[j]; if(B[j+1]==A[i]) j++; if(j==m){printf("%d\n",i-m+1);j=p[j];} } for(int i=1;i<=m;i++)printf("%d ",p[i]); return 0; }