【KMP算法】 Golang实现

KMP算法是一种改进的字符串匹配算法,它的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。

字符串的模式匹配

字符串的模式匹配是一种常用的运算。所谓模式匹配,可以简单地理解为在目标(字符串)中寻找一个给定的模式(也是字符串),返回目标和模式匹配的第一个子串的首字符位置。通常目标串比较大,而模式串则比较短小 。【百度百科】

朴素的模式匹配算法

从目标串的的第一个字符起与模式串的第一个字符比较,若相等,则继续对字符进行后续的比较,否则目标串从第二个字符起与模式串的第一个字符重新比较,直至模式串中的每个字符依次和目标串中的一个连续的字符序列相等为止,此时称为匹配成功,否则匹配失败。

若模式子串的长度是m,目标串的长度是n,这时最坏的情况是每遍比较都在最后出现不等,即每遍最多比较m次,最多比较n-m+1遍,总的比较次数最多为m(n-m+1),因此朴素的模式匹配算法的时间复杂度为O(mn)。朴素的模式匹配算法中存在回溯,这影响到匹配算法的效率,因而朴素的模式匹配算法在实际应用中很少采用。在实际应用主要采用无回溯的匹配算法,KMP算法和BM算法均为无回溯的匹配算法。

KMP中的next数组

求取模式串的从下标0到当前位置的最长公共前缀。

  • 示例·一:

    aabbaaac
    0
    01
    010
    0100
    01001
    010012
    0100122
    01001220
  • 示例二:

    issip
    0
    00
    000
    0001
    00010

KMP算法

设目标串为s,模式串为t, i、j 分别为指示s和t的指针,i、j的初值均为0。

若有 s[i ]= t[j],则i和j分别增1;否则,i不变,j退回至j=next[j]的位置 ( 也可理解为串s不动,模式串t向右移动到s[i]与t的next[j]对齐 );

比较s[i]和t[j]。若相等则指针各增1;否则 j 再退回到下一个j=next[j]的位置(即模式串继续向右移动 ),再比较 s[i]和t[j]。

依次类推,直到下列两种情况之一:

1)j退回到某个j=next[j]时有 s[i] = t[j],则指针各增1,继续匹配;

2)j退回至 j=-1,此时令指针各增l,即下一次比较 s[i+1]和 t[0]。

LeetCode刷题地址

找出字符串中第一个匹配项的下标

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

提示:

  • 1 <= haystack.length, needle.length <= 104
  • haystackneedle 仅由小写英文字符组成

基本实现

Golang实现
package main

import (
	"fmt"
)

func main() {
	s := ""
	pattern := ""
	s = "bbababaaaababbaabbbabbbaaabbbaaababbabaabbaaaaabbaaabbbbaaabaabbaababbbaabaaababbaaabbbbbbaabbbbbaaabbababaaaaabaabbbababbaababaabbaa"
	pattern = "bbabba"
	fmt.Println(kmpSearch(s, pattern))
	s = "aabaaabaaac"
	pattern = "aabaaac"
	fmt.Println(kmpSearch(s, pattern))
	s = "mississippi"
	pattern = "pi"
	fmt.Println(kmpSearch(s, pattern))
	s = "mississippi"
	pattern = "issip"
	fmt.Println(kmpSearch(s, pattern))
	s = "hello"
	pattern = "ll"
	fmt.Println(kmpSearch(s, pattern))
	s = "aaaaa"
	pattern = "bba"
	fmt.Println(kmpSearch(s, pattern))
	s = "sadbutsad"
	pattern = "sad"
	fmt.Println(kmpSearch(s, pattern))

}

func getNext(pattern string) []int {
	n := len(pattern)
	next := make([]int, n)

	j := 0
	next[0] = j

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

	return next
}

func kmpSearch(s, pattern string) int {
	next := getNext(pattern)
	fmt.Println(next)
	l := len(pattern)
	i, j := 0, 0
	for i < len(s) && j < len(pattern) {
		if s[i] == pattern[j] {
			j++
			i++
		} else if j == 0 {
			i++
		} else {
			j = next[j-1]
		}

	}
	if j == l {
		return i - j
	}
	return -1
}

参考文献

https://www.zhihu.com/question/21923021

https://blog.csdn.net/v_july_v/article/details/7041827

https://blog.sengxian.com/algorithms/kmp

https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solutions/732461/dai-ma-sui-xiang-lu-kmpsuan-fa-xiang-jie-mfbs/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北方的孤夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值