KMP匹配算法
KMP算法学习笔记
声明:下文都以S串为原串,T串为模式串,字符串都以0起始
首先介绍暴力算法(BF)
暴力不难想,两个指针i,j,分别表示在S,T中匹配到了哪一位,当j匹配到T末尾时,表示查找成功,否则,j就回退到0
时间复杂度为O(nm)
KMP算法
在介绍KMP算法之前,要引入一个函数:前缀函数
前缀函数(此处为nxt)
定义:最长的相等的真前缀与真后缀的长度
因为我们在匹配i,j的时候失败,那么j本应该回退到0,但是此时我们发现T[0...j-1]与S串的[i-j-2,i-1]是匹配的,那么我们此时应该考虑已经匹配的这一部分能否为下一次匹配贡献
考虑如下情况:
0 1 2 3 4 5 6 7
S: a b a b a b b a b
T: a b a b b
不难发现,当i=3,j=3时,下一位不匹配了,我们此时j应该回退到哪里呢?不难发现j回退到2(nxt[j])是最优的,因为已经匹配的这一部分(abab)和T串的开头(abab)是一样的,(其实就是匹配到j时,T串相同的前后缀)
那么通过解释我们应该可以看出 nxt[j]应该是所有的k满足k<j且T[0...k]=T[j-k...j]的最大值
前缀函数的求解方法:
具体讲解在代码里面
void get_nxt(string T){
int k=-1;//前缀匹配到了哪里
int j=0;//后缀匹配到了哪里
nxt[0]=-1;
int len=T.size();
while(j<len){
if(k==-1||T[k]==T[j]){//刚开始或该位匹配
if(T[++k]==T[++j])//如果下一位匹配,
nxt[j]=nxt[k];//可以类比并查集的路径压缩
else
nxt[j]=k;//不匹配那么就回退到k
}
else
k=nxt[k];//该位不匹配就回退前缀指针
}
}
KMP算法的流程
现在我们已经求出了前缀函数,那么匹配过程就十分显然了:
维护两个指针i,j,表示在S,T串中匹配到了哪里,如果不匹配,那么j回退到nxt[j]
据此不难有以下代码
int kmp(string S,string T){
int lens=S.size(),lent=T.size();
get_nxt(T);
int i=0,j=0;
while(i<lens){
if(j==-1||S[i]==T[j])
++i,++j;
else
j=nxt[j];
if(j==lent-1)//匹配成功
//进行操作
}
}
例题 剪花布条
求S串中有多少个T串
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
string S,T;
int nxt[N];
void get_nxt(string T){
int k=-1;
int j=0;
nxt[0]=-1;
int len=T.size();
while(j<len){
if(k==-1||T[k]==T[j]){
if(T[++k]==T[++j]) nxt[j]=nxt[k];
else nxt[j]=k;
}
else k=nxt[k];
}
}
int kmp(string S,string T){
int lens=S.size(),lent=T.size();
get_nxt(T);
int i=0,j=0,cnt=0;
while(i<lens){
if(j==-1||S[i]==T[j]) ++i,++j;
else j=nxt[j];
if(j==lent)
cnt++,j=0;
}
return cnt;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
string a,b;
cin>>a;
while(a!="#"){
cin>>b;
cout<<kmp(a,b)<<'\n';
cin>>a;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)