KMP算法理解
文章目录
KMP简介
KMP优于暴力匹配算法的核心是它利用了模式串本身对称(非中心对称)的特性,基于这个特性当在遇到字符失配时跳过一部分字符以达到提升查找速度的目的。这个特性是什么呢,就是字符串的前缀集合与后缀集合交集中最长字符的长度,专业术语叫部分匹配表(Partial Match Table),程序中通常命名它为next
数组。
理解了部分匹配表的含意再来看KMP的查找过就没那么难了,难点我觉得在部分匹配表(next数组)的求解过程,但是网上的文章大多不是一笔带过就是各种数学公式推导,数学不好的我读起来真烧脑,始终理解不了,直到看了这篇:KMP算法中如何计算next数组 才茅塞顿开,借助找对称的方式一下子就了然了,致敬。
next数组求解过程
假设模式串为:agctagcagctagct
,根据定义我们可以口算出next数组的值为[0,0,0,0,1,2,3,1,2,3,4,5,6,7,4],即模式串的前i+1个字符的前缀和后缀的最长公共序列的长度为next[i]。
上边的next数组是我们人肉计算得出来的,那么用程序如何求解呢?
原理就是利用next[0]~next[i]来求解next[i+1]。
为什么已知next[0]~next[i]就能得出next[i+1]呢?
a
:next[0]=0,
ag
:pattern[1]=g,pattern[0]=a,不相等,所以next[1]=0,
agc
:pattern[2]=c,pattern[0]=a,不相等,所以next[2]=0,
agc
t:pattern[3]=t,pattern[0]=a,不相等,所以next[3]=0,
agcta
:pattern[4]=a,pattern[0]=a,相等,所以next[4]=1,
agctag
:pattern[5]=g,pattern[1]=g,相等,所以next[5]=2,
agctagc
:pattern[6]=c,pattern[2]=c,相等,所以next[6]=3,
……
agctagcagctagc
:pattern[13]=c,pattern[6]=c,相等,所以next[13]=7
问题来了
agctagcagctagct
:pattern[14]=t,pattern[7]=a,不相等。
怎么办?于是next[14]=0吗?很显然agct
才是前缀和后缀的最长共同序列,next[14]=4。
怎样找到agct
呢?
原理就是找次长公共序列。
图中橙色的A表示已经确定的最长公共序列,绿色的T将要与开头的A后面的元素进行比较,如果比对失败,我们需要寻找次长公共序列B,然后T再与开头的B后面元素进行比对。
为什么要找次长公共序列呢?
从上图橙色块都是一样的,假如存在次长公共序列,第二幅图表明橙色块必然同时以B开头且以B结尾即,第三幅图所示,这就得出T位置的次长公共序列长度就是橙色块A的最长公共序列长度。
因此,计算agctagcagctagc
的次长公共序列就是要计算A=agctagc
的最长公共序列,而这个已经计算过了,next[6]=3,得到agc。
通过图,借助找对称也就理解了next数组的求解原理。
Golang实践算法
|
|
代码未做优化,主要用来理解KMP的原理。
参考
https://www.jianshu.com/p/6c53d06d23cc
https://github.com/thzt/algorithm.js/blob/master/other/kmp/kmp.js
https://blog.csdn.net/yearn520/article/details/6729426
https://www.cnblogs.com/c-cloud/p/3224788.html
文章作者 XniLe
上次更新 2023-10-10