GO语言实现KMP算法

前言

本文结合朱战立教授编著的《数据结构---使用c语言(第五版)》(以下简称为《数据结构(第五版)朱站立》)中4.4.2章节内容编写,KMP的相关概念可参考此书4.4.2章节内容。原文中代码是C语言,本文改用Go语言编写,逻辑不变。

一、KMP介绍

‌KMP算法‌(Knuth-Morris-Pratt算法)是一种高效的字符串匹配算法,由D.E.Knuth、J.H.Morris和V.R.Pratt提出。该算法的核心在于利用匹配失败后的信息,尽量减少模式串与主串的匹配次数,以达到快速匹配的目的。KMP算法的时间复杂度为O(m+n),其中m和n分别代表模式串和主串的长度。

二、求next数组

next数组是KMP算法中核心概念,求出next数组后,在模式串元素和主串元素比较的失败的时候,可以将模式串t直接偏移到以next数组中的元素为下标的位置,主串指针不偏移,减少了元素比较次数,下面我们直接求next数组

举例

字符串s="ababbababcabac"

模式串t="ababcab"

go语言版求next数组的代码如下:

go 复制代码
func GetNext(s string) []int {
	var next []int = make([]int, len(s))
	next[0] = -1
	next[1] = 0
	// j表示当前要求next值的位置
	// k表示当前要和前一个字符比对的下标
	j, k := 1, 0
	for j < len(s)-1 {
		if s[j] == s[k] {
			next[j+1] = k + 1
			k++
			j++
		} else if k == 0 {
			next[j+1] = 0
			j++
		} else {
			k = next[k]
		}
	}
	return next
}

执行上面代码我们能获取到next数组如下:

c 复制代码
PS D:\Project\GoWorkSpace\DataStructure> go run .\0113\KMP_Algorithm.go
[-1 0 0 1 2 0 1]

注:以上代码的变量名称,逻辑均与《数据结构(第五版)朱站立》中内容一致,方便代码阅读

三、图解KMP匹配过程

中间部分循环过程省略,主要看当模式串和主串不相等时,模式串如何偏移

字符串s="ababbababcabac"

模式串t="ababcab"

next数组=[-1 0 0 1 2 0 1]

第一步:s数组元素和t数组元素一一对比,如果成功两个数组下标偏移同时+1继续比较,以此类推

第二步:当s[4]≠t[4]时,next[4]=2,s数组不偏移,t数组偏移到t[2],比较s[4]和t[2]

第三步:当s[4]≠t[2]时,next[2]=0,s数组不偏移,t数组偏移到t[0],比较s[4]和t[0]

第四步:当s[4]≠t[0]时,由于t数组已经到第一位,所以s数组下标+1,比较s[5]和t[0]

第五步:当s[5]=t[0]时,回到了第一步,两个数组下标偏移同时+1继续比较,以此类推;当t的下标为7时s的下标为12,循环结束打印t的第一个元素在s中下标的位置,即:12-7=5

四、Go示例代码

go 复制代码
package main

import "fmt"

func KMP(s string, t string) int {
	m := len(s)
	n := len(t)
	// s中当前比对的位置是i
	// t中当前比对的位置是j
	i, j := 0, 0
	next := GetNext(t)
	for i < m && j < n {
		if s[i] == t[j] {
			i++
			j++
		} else if j == 0 {
			i++
		} else {
			j = next[j] // t串偏移,偏移到next[j]下标
		}
	}
	if j == n { // t串全部匹配
		return i - j
	} else {
		return -1
	}
}
func GetNext(s string) []int {
	var next []int = make([]int, len(s))
	next[0] = -1
	next[1] = 0
	// j表示当前要求next值的位置
	// k表示当前要和前一个字符比对的下标
	j, k := 1, 0
	for j < len(s)-1 {
		if s[j] == s[k] {
			next[j+1] = k + 1
			k++
			j++
		} else if k == 0 {
			next[j+1] = 0
			j++
		} else {
			k = next[k]
		}
	}
	return next
}
func main() {
	t := "ababcab"
	fmt.Println(GetNext(t))
	fmt.Println(KMP("ababbababcabac", t))
}

运行结果

c 复制代码
PS D:\Project\GoWorkSpace\DataStructure> go run .\0113\KMP_Algorithm.go
[-1 0 0 1 2 0 1]
5
相关推荐
{⌐■_■}1 小时前
【gRPC】HTTP/2协议,HTTP/1.x中线头阻塞问题由来,及HTTP/2中的解决方案,RPC、Protobuf、HTTP/2 的关系及核心知识点汇总
网络·网络协议·计算机网络·http·rpc·golang
珊瑚里的鱼1 小时前
【滑动窗口】LeetCode 1658题解 | 将 x 减到 0 的最小操作数
开发语言·c++·笔记·算法·leetcode·stl
落樱弥城1 小时前
角点特征:从传统算法到深度学习算法演进
人工智能·深度学习·算法
共享家95271 小时前
哈希的原理、实现
c++·算法
进击的小白菜1 小时前
用Java实现单词搜索(LeetCode 79)——回溯算法详解
java·算法·leetcode
珂朵莉MM2 小时前
2024 睿抗机器人开发者大赛CAIP-编程技能赛-专科组(国赛)解题报告 | 珂学家
开发语言·人工智能·算法·leetcode·职场和发展·深度优先·图论
小智学长 | 嵌入式2 小时前
进阶-数据结构部分:2、常用排序算法
java·数据结构·算法
少了一只鹅2 小时前
字符函数和字符串函数
c语言·算法
Dr.9273 小时前
1-10 目录树
java·数据结构·算法
子豪-中国机器人3 小时前
C++ 蓝桥 STEMA 省选拔赛模拟测试题(第一套)
开发语言·c++·算法