寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄寄|

TLE_Automation

园龄:2年9个月粉丝:19关注:23

KMP

感谢 @奇乐编程学院 的图。

KMP 算法,是一种利用已经知道的信息来避免重复运算的字符串匹配算法。

KMP 的算法流程

对于字符串匹配,暴力算法是一位一位的比较,但不同时,将起始指针加 1。然后继续匹配,复杂度为 O(nm)

那我们就想,再没有匹配上的这一位之前的所有位都走过了,真的有必要按暴力走吗?

我们定义一个 nxt 数组,nxti 表示前 i1 位中 最长的公共前后缀 的长度。

那为什么要这样设计呢?

比如这张图,当你往下匹配的时候。

发现到了 C 不匹配了,我们可以将子串移动到这个位置:

为什么这样移动是对的,你发现 C 前面的最长公共前后缀是 AB,而之前的 AB 已经匹配上了。

你这一段的后缀是和前缀相等的,而后缀已经匹配成功了,那前缀和他相等,是不是也匹配成功了,那不是就不需要匹配了吗?

直接跳过来就行了。

nxt 数组的求法

那问题来了,怎样去求解这个 nxt 数组,这也是 KMP 的难点。

nxt 数组就是在子串中找出所有 i1 的公共前后缀长度,如果这一步用暴力求解,那这就不能保证他的复杂度为 O(n+m)了。

比如这个字串,假如我们当前已经匹配到了 ABACAB

当指针往右移动,发现还是匹配的,那长度不久等于之前的加 1 吗。

如果不匹配了,那当前这个构不成最长的,那我找个比他短的不就行了吗。

举个例子:

现在我们匹配到了 ABACABA 指针向右一看,BC 不匹配,而我们已经匹配完了 ABACABA

这时的最长的公共前后缀为 ABA

因为前缀和后缀是完全相等的,那 前缀的后缀后缀的后缀 也是完全相等的。

我们可以直接找他前缀的最长的前后缀不就行了吗?

代码实现:

void kmp() {
j = 0; // j 是子串的指针
for(int i = 2; i <= Len2; i++) { // 这里从 $2$ 开始循环的原因是 $nxt_1$ 一定是 $0$,因为如果他遍历过的只有 $1$ 位。
while(j && ch[i] != ch[j + 1]) j = nxt[j]; // 当不匹配的时候往前跳。
if(ch[i] == ch[j + 1]) j++; 匹配的时候直接加
nxt[i] = j;
}
}
for(int i = 1; i <= Len1; i++) {
while(j && s[i] != ch[j + 1]) j = nxt[j];
if(s[i] == ch[j + 1]) j++;
if(j == Len2) printf("%d\n", i - Len2 + 1), j = nxt[j];
}

同理可得。

本文作者:TLE_Automation

本文链接:https://www.cnblogs.com/tttttttle/p/16378673.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   TLE_Automation  阅读(35)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起