KMP算法

KMP算法是什么

借助ChatGPT,咨询对KMP算法的解释:
KMP算法是一种用于字符串匹配的高效算法,它可以在文本串中查找某个模式串的出现位置。KMP算法的核心思想是利用已经匹配过的部分信息来避免不必要的字符比较,从而提高匹配的效率。其详细解释如下:

构建部分匹配表(Partial Match Table):首先,针对模式串,KMP算法会构建一个部分匹配表,也称为前缀函数或失配函数。这个表记录了模式串中每个位置对应的最长相等前缀和后缀的长度。这个表的构建是KMP算法的关键步骤。

进行匹配:在匹配过程中,将模式串与文本串逐个字符比较,但当发生失配时,KMP算法不会像朴素的字符串匹配算法那样将模式串向右移动一位,而是利用部分匹配表来决定模式串应该移动多远。

利用部分匹配表:当发生失配时,根据部分匹配表,可以知道模式串应该向右移动多少位以继续匹配,而不会漏过可能的匹配位置。具体来说,根据失配字符的位置,查找部分匹配表中对应位置的值,这个值表示了模式串应该向右移动的位数。

继续匹配:然后,根据部分匹配表中的值,将模式串向右移动,使得模式串的一部分与文本串中的字符对齐,然后继续比较字符。

重复步骤:重复上述步骤,直到找到匹配或者模式串无法再向右移动为止。如果找到了匹配,就记录匹配的位置,然后继续寻找下一个匹配。

借助上面的描述,简单的说,KMP算法就是在文本串A中查找是否存在文本串B,可以将文本串B称为匹配串。其具体做法是:先对匹配串B进行处理,得到一个部分匹配表,这个表将在后续匹配中告诉我们应该怎样移动匹配位置。假设A长度为N,B长度为M。若采用暴力解法需要时间复杂度为O(N*M),而KMP通过部分匹配表优化匹配逻辑,时间复杂度为O(N).

若是理解了时间复杂度是怎么缩减的那就基本上掌握了KMP.所以我们可以先看可以怎样缩减暴力解法的时间复杂度。暴力搜索代码如下:

def search(str1, pat1):
	n, m = len(str1), len(pat1)
	for i in range(n - m + 1):
		if str1[i:i+m] == pat1:
			return True
	return False

该方法从A串每个位置开始找接下来的M个字符是否等于匹配串B,其实A中的每个位置的数据被多次使用,若可以在每次使用后记录一下部分信息使得后续可以不在使用该位置数据,那时间复杂度就可以降低。更细致地,在A的第i个位置开始判断接下来m个字符是否和匹配串相等,即(Ai,B0),(Ai+1,B1),…(Ai+m-1, Bm-1)是否一一相等,我们可以依次遍历这m个对,若直到位置j(j>0),才出现(A[i+j] !=B[j]),这时我们能确定前j-1个数是一致,但是匹配串不会从位置i和A想匹配,这时我们可以从i+1位置从头匹配匹配串B,也可以根据j-1数据的信息从i+j开始匹配。
后者对于长串A每个字符仅遍历一次,时间复杂度明显优于前者。但是我们不能单纯的用i+j位置作为开头来和B匹配,因为可能存在k使得A[i+j-k: i+j]= B[:k], 若此时A[i+j]=B[k],那么有可能A[i+j-k: i+j-k+m] = B。由于j是A[i]开始不等于最早出现的位置,那么可知A[i+j-k: i+j]=B[j-k: j],所以k满足B[j-k: j]=B[:k], 所以只要找到中找到B串中j位置满足上式的最长k, 再去对比A[i+j]和B[k]就能避免上述情况发生。若k=0,那么比较的正是A[i+j]=B[0]。

若前面的都能理解,那么就存在最后一个问题,如何生成B串中每个j对应的最长k?该问题可以通过动态规划算法来做,j从1到m-1,dp[j] 表示j位置最长的k,使得B[j-k: j] = B[:k],
那么状态转移逻辑是 dp[j+1] = (B[j]==B[t])? t + 1 : 0 其中 t是从dp[j]到0 开始找到的第一个t使得B[j-t: j+1] =B[:t+1], 此处可以采用递归方式保证B[j-t: j]=B[:t] 。
由于在位置0其前面没有字符,所以dp[0]=0, 由此我们可以推算出dp[1:m]。

完整代码如下:

def get_pmt(pat1):
	n = len(pat1)
	dp = [0] * (n+1)
	for i in range(1, n+1):
        t = dp[i]
        while t>0 and pat1[i+1] != pat1[t]:
            t = dp[t-1]
        dp[i] = t + 1 if pat1[i] == pat1[t] else 0
	return dp

def search(str1, pat1):
	dp = get_pmt(pat1)
	i, j = 0, 0
	while i<len(str1) and j<len(pat1):
		if str1[i] != pat1[j] and j==0:
			i+=1
		elif str1[i] != pat1[j]:
			j = dp[j]
		else:
			i, j = i+1, j+1
	if j == len(pat1) and str1[i-j: i] == pat1:
		return True
	return False

至此,KMP算法已讲完,总结,即先生成pmt列表,再通过该列表进行快速便利,时间复杂度总共O(M+N) 当N>>M 时复杂度为O(N)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值