KMP 算法

写作时间:2023/1/30/16:33

1. KMP 算法是什么

  • 问题:

    给出两个字符串 s1s2,若 s1 的区间 [l,r] 子串与 s2 完全相同,则称 s2s1 中出现了,其出现位置为 l
    定义一个字符串 s 的 border 为 s 的一个s 本身的子串 t,满足 t 既是 s 的前缀,又是 s 的后缀。
    现在请你求出 s2s1 中所有出现的位置。对于 s2,你还需要求出对于其每个前缀 s 的最长 border t 的长度。

  • KMP 可以在 O(N+M) 的时间内解决如上的字符串匹配的问题。(N,M 分别是两个字符串的长度)

2. 关于 nxt 数组

2.1 定义

对于字符串 S, nxti 表示 :S[1...i] 中最长的相等真前缀与真后缀的长度。(根据定义即可发现,nxti<i
例如,对于 S=ababac, nxt1=0,nxt2=0,nxt3=1,nxt4=2,nxt5=3,nxt6=0。(ps. nxt 数组其实对应的就是 border

2.2 引理※

  • 引理 1:nxti(对于 nxti>1) 所对应的相等真前缀与真后缀是由 S[1...i1] 中的一个相等真前缀与真后缀扩展而来的,当然,不一定是由 S[1...i1] 中那个最长的相等真前缀与真后缀扩展而来的。

  • 引理 2:nxti, nxtnxti, nxtnxtnxti....,分别对应 S[1...i] 中所有相等真前缀与真后缀的依次递减的长度。※

2.2 nxt[i] 怎么求?

首先,假设我们已知 nxt1..nxti1

根据引理 1, 有这样一个思路:枚举所有 S[1...i1] 的相等真前缀与真后缀,枚举顺序是按长度依次递减。设当前枚举到的相等真前后缀的长度是 l, 若发现可以扩展,即 Sl+1=Si, 则 l 就是 nxti 的答案。

至于怎么枚举所有 S[1...i1] 的相等真前后缀,根据引理 2, 可知,nxti1,nxtnxti1... 构成了所有 S[1...i1] 的相等真前后缀长度。又因为 nxtii1 且我们已知 nxt1...nxti1, 所以我们知道 nxti1,nxtnxti1...

求 nxt 数组的代码
void getnxt(){
	for(int i = 1; i <= n; i++) nxt[i] = 0;
	nxt[0] = -1;
	for(int i = 2, j; i <= n; i++){
		j = nxt[i - 1];
		while(a[j + 1] != a[i] && j != -1) j = nxt[j];			
		if(j != -1) nxt[i] = j + 1;
	}	
}

有了 nxt 数组,接下来就是利用 nxt 数组解决匹配问题。
(先说明一下,后面提到的 S 是文本串,T 是模式串,len1,len2 分别是 ST 的长度)

3. KMP 算法流程

现在有两个指针 i,j, 分别表示 当前要匹配 Si, Tj。接下来,分类讨论。

  • Si=Tj

    • j=len2:记录答案,j = nxt[j] + 1; i++;中间直接跳过※
    • jlen2i++; j++;
  • SiTj

    • j=1i++; j++;
    • j>1j = nxt[j - 1] + 1;
posted @   Jiayn  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示