【手敲算法】kmp 理解 到 裸敲

为什么要学习kmp and 算法的作用

给出两串 字符串,模式串T(长度m),匹配串S(长度n)。在S中快速找到T串的位置

暴力破解 是 O(n * m) ——枚举起始点逐个匹配

kmp算法可以在线性时间O(n + m)下解决问题

算法原理

  • kmp根据 前缀表 和 next数组 来实现

前缀表是取出 T 串的所有前缀

  • 对每一个前缀(后称pre)进行如下操作:

找出pre的前缀 和 pre的后缀,他们的长度均小于等于 len(pre)  (即 不能是原串pre)

找出 当  pre前缀 == pre后缀  时,的最大长度

  • 如何递推求出这个前缀表:

假设我们已经知道 某一段字符串,当新增下一个字符变成newPre时,分两种情况

为了方便例子的演示,1 和 2 中配图内字符不完全相同(橙色表示匹配成功的当前pre的前后缀)

1. 新增字符 == id下标(len(pre的前缀 )+ 1) 的字符

这时候直接 len + 1 即可

2.新增字符 != id下标(len(pre的前缀 )+ 1) 的字符

那么回退匹配串 到 上一层的最大匹配,继续比较,直到匹配成功,or回退致空串


到此为止 我们将后缀表的原理弄清楚了 后缀表 的原理

那么后缀表整理为

abaabcac
00112010
每个字符下是  pre  的 最长匹配 maxL

next数组则整体右移 1 位,第一个字符下 填补 -1

这就是next数组

abaabcac
-10011201

这些数字就是索引的下标,如果匹配S串的过程中(S[ i ]),出现不匹配

那么直接跳转到这个T[id] 字符下 数字 id[ j ]  的 的字符处,再次比较 T[id [ j ] ] 和 S[ i ] 是否匹配,依次列推

直到匹配成功,或者 跳到-1 (即无处可跳,这里的跳就是 缩短pre的串 的一个过程)

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
const int inf = 1e9 + 7;

int nxt[maxn];
char S[maxn], T[maxn];

void getNxt(char *str, int len){
	nxt[0] = -1;
	int k = -1;
	for(int q = 1 ; q <= len - 1 ; q++){
		while(k > -1 && str[k+1] == str[q]){
			k = nxt[k];
		}
		if(str[k+1] == str[q]){
			k++;
		}
		nxt[q] = k;
	}
}

int KMP(char *str, char *ptr){
	//menset(nxt, 0, sizeof(nxt));
	int slen = strlen(str);
	int plen = strlen(ptr);
	getNxt(ptr, plen);
	int k = -1;
	for(int i = 0 ; i < slen ; i++){
		while(k > -1 && ptr[k+1] != str[i]){
			k = nxt[k];
		}
		if(ptr[k+1] == str[i]){
			k = k+1;
		}
		if(k == plen-1){
			return i - plen + 1;
		}
	}
	return -1;
}

int main()
{
	KMP(S , T);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值