简单理解KMP - 三分钟复习算法Go

KMP - 字符串匹配算法

前言:其实本来没有打算写KMP算法,毕竟这种课本上的基础算法,想必大家也都了解。但是某夜刷题遇到时竟苦苦思索不得正解,网上文章又大多杂乱无章,或代码不得正解,故写下此篇以便来日复习


代码先行

算法就是拿来用的,没有代码的算法算什么
以下是用Go语言实现的KMP算法

func kmp(haystack string, needle string) int {
    //when needle is empty,return 0 is available
	if len(needle) == 0 {
		return 0
	}

	next := getNext(needle)

	i := 0
	j := 0
	for i < len(haystack) && j < len(needle) {
		if j == -1 || haystack[i] == needle[j] {
			i++
			j++
		} else {
			j = next[j]
		}
	}

	if j == len(needle) {
		return i - j
	}

	return -1
}

//original getNext function
func getNext(str string) []int {
	var next = make([]int, len(str))
	next[0] = -1

	i := 0
	j := -1
	for i < len(str) - 1 {
		if j == -1 || str[i] == str[j] {
			i++
			j++
			next[i] = j
		} else {
			j = next[j]
		}
	}

	return next
}

//better getNext function
func getNext(str string) []int {
	var next = make([]int, len(str))
	next[0] = -1

	i := 0
	j := -1
	for i < len(str) - 1 {
		if j == -1 || str[i] == str[j] {
			i++
			j++
			if str[i] == str[j] {
				next[i] = next[j]
			} else {
				next[i] = j
			}
		} else {
			j = next[j]
		}
	}

	return next
}
  • 需要注意的是这里有两个getNext函数,其中一个是前者的改良版
  • 其他语言写法一样,稍稍改变即可

边做边学

  • 现在,给你一道算法题,给出两个字符串A和B,问,A中是否存在B,如果存在输出起始位置,不存在则输出-1
  • 对于大多数高级语言来说,它们已经提供了很多方法为我们省去了这些繁琐的过程,如Java的indexOfPHP的strstr
  • 或者就算不依赖于这些,我们也可以采用暴力扫描法,当时,其中的时间复杂度可想而知
func strstr(haystack string, needle string) int {
	i := 0
	j := 0
	for i < len(haystack) && j < len(needle) {
		if haystack[i] == needle[j] {
			i++
			j++
		} else {
			//move one step
			i = i - j + 1
			j = 0
		}
	}

	if j == len(needle) {
		return i - j
	}

	return -1
}
  • 暴力破解法的特点在于,两者字符串发生不匹配时,选择是将主串指针向前移动一位,并重置匹配串指针,KMP的巧妙之处正在于此,通过移动匹配串来快速找到匹配位置

走进核心

至少至此,我们至少知道了KMP为何而来,以及它解决了什么, 但是,KMP的next数组到底是什么?

  • PMT:Partial Match Table,部分匹配表,即我们在KMP算法中,去计算得出的next数组
  • 以字符串 ababcabef 为例
    简单理解KMP - 三分钟复习算法Go_第1张图片
  • 在这里我们需要了解,字符串的前缀后缀,这个好理解,对于hello来说,它的前缀包括了{“h”,“he”,“hel”,“hell”},它的后缀包括了{“o”,“lo”,“llo”,“ello”}
  • 对于PMT来说,它的value是由前缀集合和后缀集合中,最长的交集字符串长度,如对aba来说最长交集字符串自然是a,因此index=2的value=1
  • next数组就是在此的基础上,除去当前字符的最长前缀集合和后缀集合交集的最长字符串长度,其实看数据也可以发现,next的值相较于value整体右移了一个单位,并将next[0]赋值为-1

图解搜索

  • 现在,我们来尝试在 该字符串 abcababc 中去搜索 ababc,这里的next数组根据上表可以看出是next={-1,0,0,1,2}
    简单理解KMP - 三分钟复习算法Go_第2张图片
    简单理解KMP - 三分钟复习算法Go_第3张图片
    简单理解KMP - 三分钟复习算法Go_第4张图片
  • 我们这时候根据next数组,将next[j]赋值给j,即j=0
    简单理解KMP - 三分钟复习算法Go_第5张图片
  • 这时候仍然不匹配,根据next数组j=-1,则i和j都加一
    简单理解KMP - 三分钟复习算法Go_第6张图片
  • 最终匹配到了搜索串末尾
    简单理解KMP - 三分钟复习算法Go_第7张图片
  • 最终 ,我们找到的位置就是 i - j

KMP优化

  • 在计算next数组时,如果a字符与它next值指向的b字符相等,则a字符的next值指向b字符的next值
  • 如对于ABABC来说,index=3时,next值应该等于1,优化后指向0
  • 提高了KMP的效率

你可能感兴趣的:(算法)