算法笔记(2):KMP 算法
介绍
KMP算法(全称Knuth-Morris-Pratt字符串查找算法,由三位发明者的姓氏命名)是可以在文本串
思想
考虑
缺点在于每次都要重新开始逐一匹配,不能充分利用前面匹配得到的信息,导致算法效率低下
那么我们需要考虑的问题是 匹配失败后从哪一位开始重新匹配最好?
首先,我们需要引入一些概念
前缀函数
子串:
,即
前缀:
真前缀:【不包含 本身】
后缀:
真后缀:【不包含 本身】
公共前后缀:
真公共前后缀:【不包含 本身】
定义 前缀函数
即
怎么维护?
-
暴力:枚举前缀子串
,枚举该串的前后缀 ,比较 ,总 -
注意到
的真公共前后缀 移除最后一位后的串 是 的真公共前后缀,我们可以根据这个构造递推方程对于
,
如果 ,
否则因此我们只需要从大到小枚举
的所有真公共前后缀进行匹配就可以得出 的最长真公共前后缀
那么如何从大到小枚举 的所有真公共前后缀?
假设下一个真公共前后缀 ①
因为
即 ②
那么 ①②如
假设 = ①
因为 =
即 = ②
那么 = ①②由于
且 这是下一个真公共前后缀
即 【 】
那么找下标为 的下一个真公共前后缀 找下标为 的最长公共前后缀综上,从大到小枚举
的所有真公共前后缀只需要不断地将 不断循环直到成功匹配或者枚举到-1即可因此,求
,令 ,按照上面的枚举方式不断循环即可
如果成功匹配,那么
否则代码
点击查看代码
vector<int> getSuffixFunction(string S){//0-based //"aabaaab" int n = S.size(); vector<int> pi(n); for(int i = 0; i < n; i ++ ){ //递推求pi int k = i-1; while(k != -1 && S[pi[k]] != S[i])k = pi[k]-1; //从大到小枚举S[0..k]的所有真公共前后缀 //k == -1:说明枚举过程结束 //S[pi[k]] == S[i]:说明匹配成功 if(k == -1 || S[pi[k]] != S[i])pi[i] = 0; //匹配失败 else pi[i] = pi[k] + 1; //匹配成功 } return pi; //[0,1,0,1,2,2,3] }
理解前缀函数的递推后,字符串匹配就简单了
将模式串
模式串在文本串中的条件为
如
模式串= "ABA"
文本串= "ABABA"
新串:
下标:
:
代码
点击查看代码
vector<int> KMP(string S,string P){//0-based
S = P + "?" + S;
int n = S.size();
vector<int> pi(n);
vector<int>res;
for(int i = 0; i < n; i ++ ){
int k = i-1;
while(k != -1 && S[pi[k]] != S[i])k = pi[k]-1;
if(k == -1 || S[pi[k]] != S[i])pi[i] = 0;
else pi[i] = pi[k] + 1;
if(pi[i] == P.size())res.push_back(i - 2*P.size());
//返回首位
}
return res;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探