KMP算法解析

KMP算法用来匹配一个字符串是否包含一个指定字符串;

  • KMP算法的核心是得到子串的部分匹配表;在计算当出现不匹配时子串要回溯的位置时需要用到;

  • 部分匹配值:”就是”前缀”和”后缀”的最长的共有元素的长度。
    以子串ABCDABD为例,来得到他的部分匹配表

    首先弄清楚前缀与后缀的概念;前缀指去除尾部字符的全部组合;后缀指去除首部字符的全部组合;
    比如:ABCDA的前缀有[“A”,“AB”,“ABC”,“ABCD”],后缀有[ “BCDA”, “CDA”, “DA”, “A”]

  • 按照部分匹配值的定义计算出ABCDABD的部分匹配表为

    ABCDABD
    0000120
    • “A”的前缀和后缀都为空集,共有元素的长度为0;
    • “AB”的前缀为[A],后缀为[B],共有元素的长度为0;(即AB;共有元素长度为0)
    • “ABC”的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;(即AB和 BC;共有元素的长度0)
    • “ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0(即ABC和 BCD;共有元素的长度0);
    • “ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为”A”,长度为1(即ABCD和 BCDA;共有元素的长度1);
    • “ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2(即ABCDA和 BCDAB;共有元素的长度2);
    • “ABCDABD”的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。(即ABCDAB和 BCDABD;共有元素的长度0)
    • 根据公式:字串需要回溯的长度=匹配的字符长度-最后一个匹配字符对应的”部分匹配值”
      从而得到需要回溯到的位置索引
      //go代码实现返回他的Next切片
      func Next(needle string) []int {
      	l := len(needle)
      	next := make([]int, l)
      	next[0] = -1
      	k := -1
      	i := 0
      	for i < l-1 {
      		if k == -1 || needle[k] == needle[i] {
      			i++
      			k++
      			next[i] = k
      		} else {
      			k = next[k]
      		}
      	}
      	return next
      }
      

举例:

假定求字符串:BBCABCDABABCDABCDABDE(母串)中是否包含ABCDABD(子串)

在开始计算之前需要得到子串的部分匹配值

  1. 从符串BBCABCDABABCDABCDABDE第一位开始匹配ABCDABD第一位;符合的话就匹配ABCDABD的第二位,以此类推

  2. 按照第1步逻辑我们走到母串的索引为3的位置;BBCABCDABABCDABCDABDE;发现最后的A与字串最后的D不符合;按照暴力匹配来算,可能得从母串的索引为4的位置再重复第1步;但是按照KMP算法的子串部分匹配值;用匹配的字符长度-最后一个匹配字符对应的”部分匹配值”得到字串需要回溯的长度;

    回溯的长度=6-2;指针指到C从C开始匹配

  3. C与索引9的A不匹配;还需要回溯:2-0=2;回到字串的第一位A;

  4. A与母串的索引9相同;重复第一步;

  5. 以此循环往复;最终走到母串的索引19;BBCABCDABABCDABCDABDE;完成匹配;这时候也可以把字串给回溯7-0位重新开始重复第1步;试图寻找是否还存在匹配

根据Next返回的切片计算出字符串匹配的位置

//KMP字符串匹配
func SindexKMP(S,T string) int {
	next:=NextArray(T)
	i := 0
	j := 0
	//同时满足才可以  找除字符串出现的第一个位置
	for ;i <= len(S) -1  && j <= len(T) -1;{
		if j == -1|| S[i] == T[j]{
			//当字符匹配时 i j 都加1
			i++
			j++
		}else{
			//子串的 偏移量 从next数组中取  i 不变
			j = next[j]
		}
	}
	//如果 j 大于 或者 等于 T串的长度 说明匹配成功
	if j >= len(T) -1 {
		return i - len(T) +1
	}
	return 0
}

调用:

func  main(){
	str:="ABCDABD"
	array := Next(str)
	fmt.Println(array)
	T:="BBCABCDABABCDABCDABDE"
	kmp := SindexKMP(T, str)
	fmt.Println(kmp)
	fmt.Println(T[kmp-1:kmp+6] )
}
/*
output:
	[-1 0 0 0 0 1 2]
	14
	ABCDABD
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值