KMP算法

KMP 算法的核心思想,跟 BM 算法非常相近。在模式串和主串匹配的过程中,把不能匹配的那个字符仍然叫作坏字符,把已经匹配的那段字符串叫作好前缀。
在这里插入图片描述从模式串和主串头部开始匹配,遇到坏字符时模式串向后滑动,可滑动多少呢,这就是KMP算法的关键。好前缀的后缀子串 与 主串的前缀子串 匹配存在时,模式串就不能向后滑动模式串长度个位置了。
在这里插入图片描述在这里插入图片描述
为了能够快速得到不匹配是向后滑动的位数,我们先构建了next数组。数组下标是模式串前缀子串结尾字符下标,值是最长可匹配前缀串结尾字符下标。

在这里插入图片描述在这里插入图片描述计算next为了效率使用递归的思想。如果 next[i-1]=k-1,也就是说,子串 b[0, k-1]是 b[0, i-1]的最长可匹配前缀子串。如果子串 b[0, k-1]的下一个字符 b[k],与 b[0, i-1]的下一个字符 b[i]匹配,那子串 b[0, k]就是 b[0, i]的最长可匹配前缀子串。所以,next[i]等于 k。如果 b[0, k-1]的下一字符 b[k]跟 b[0, i-1]的下一个字符 b[i]不相等,我们就可以考察 b[0, i-1]的次长可匹配后缀子串 b[x, i-1]对应的可匹配前缀子串 b[0, i-1-x]的下一个字符 b[i-x]是否等于 b[i]。如果等于,那 b[x, i]就是 b[0, i]的最长可匹配后缀子串。如果不等我们继续找b[0, i-1]的次次长可匹配后缀子串 b[r, i-1]对应的可匹配前缀子串 b[0, i-1-r]的下一个字符 b[i-r]是否等于 b[i]。如果等于,那 b[r, i]就是 b[0, i]的最长可匹配后缀子串。就这样要么找到,要么找不到。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

func getNexts(pattern string) []int {
	m := len(pattern)
	nexts := make([]int, m)
	for index := range nexts {
		nexts[index] = -1
	}

	for i := 1; i < m-1; i++ {
		j := nexts[i-1]

		for pattern[j+1] != pattern[i] && j >= 0 {
			j = nexts[j]
		}

		if pattern[j+1] == pattern[i] {
			j += 1
		}

		nexts[i] = j
	}

	return nexts
}

func findByKMP(s string, pattern string) int {
	n := len(s)
	m := len(pattern)
	if n < m {
		return -1
	}

	nexts := getNexts(pattern)

	j := 0
	for i := 0; i < n; i++ {
		for j > 0 && s[i] != pattern[j] {
			j = nexts[j-1] + 1
		}

		if s[i] == pattern[j] {
			if j == m-1 {
				return i - m + 1
			}
			j += 1
		}
	}

	return -1
}

以上内容摘自《数据结构与算法之美》课程,来学习更多精彩内容吧。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值