BF算法(暴力求解)
我们直接看一个例子:
文本串: a b c b e f g
模式串: b e f
题目:在文本串中找到模式串并返回其所在位置.
整体来说就是不断从文本串的不同位置开始与模式串匹配,直到匹配上.
KMD算法
好,我们改变一下题目:
文本串:a b a a b a a d e
模式串:a b a a d e
题目:在文本串中找到模式串并返回其所在位置.
BF算法当然可以解决,但是分析一下,需要判断4次才能得到正确起始位置.
这样,我们引入了KMP算法,它可以高效的解决这类文本串含多个模式串起始字符的查找问题.
下面给出思路:
首先,我们先把两个字符串匹配一次:
如图,匹配到 i 位置停止.
下一步, i 不动,从模式串0~j -1 号位之间分别找以0号位开头的字符串(记为s1)和以 j-1 号位结尾的字符串(记为s2),这两个字符串必须相等(尽可能长),且长度不能为 j .
如图:s1的长度正好就是 j 要移动到的位数,也就是是1号位.
j 和 i 继续匹配,而此时我们检查的正是从文本串3号位开始的字符串是否与模式串匹配,其中3号位肯定匹配.
为什么这样做?
我们拉回之前的图:
把下一次文本串的起始位置记为start ,start = 2/3.
首先,既然已经匹配失败停止到 i 位上,那么模式串肯定比 0 ~i -1(0~j-1)长,更比start~i-1(j-1)长,所以如果起始位置为start的文本串能和模式串匹配成功的话arr1[start]~arr1[i-1]肯定能和模式串匹配(总体相等,部分也相等),如果连arr1[start]~arr1[i-1]都匹配不上也就没必要继续了(自己举个反例子体会一下).而arr1[start]~arr1[i-1]匹配的一部分模式串为arr2[0]~arr2[i-start],即arr1[start]~arr1[i-1]==arr2[0]~arr2[i-start](1)
又因为arr1[0]~arr1[i-1]==arr2[0]~arr[ j-1],所以必有arr1[start]~arr1[i-1]==arr2[start]~arr2[ j-1](2)(总体相等,部分也相等)
联立(1)(2)得arr2[0]~arr2[i-start]==arr2[start]~arr2[ j-1]
显然,3满足,2不满足,所以start=3,
这解释了
从模式串0~j -1 号位之间分别找以0号位开头的字符串(记为s1)和以 j-1 号位结尾的字符串(记为s2),这两个字符串必须相等(尽可能长)
至于尽可能长,则是因为越长,文本串起始位置越往前,防止漏掉,当然匹配不上就短一点,就像上文start=3却!=2.
且长度不能为 j
如果长度为 j 的话,那不就是刚匹配过的字符串,很显然匹配失败.
直到现在我们终于可以把 j 移动了
如图:s1的长度正好就是 j 要移动到的位数,也就是是1号位.
然后比较 i 和 j 是否匹配
这解释了为什么 i 不用移动!!!(看(2)式,前面都相等了,i 还退回去比个集贸)
最后一个疑问,确定 j回退的位置貌似更加繁琐.可惜,前人为我们创造了next数组,可以显示出每个位置的回退位置,它被公式推导出来.
给next数组赋值的推导过程:
首先,无论何时
next[0]=-1(在下文)
next[1]=0;
下一步
设next[i]=k(k为 j 回退到的位置);
arr2[0]~arr2[k-1]==arr2[start]~arr2[ j-1]
观察发现start = j - k;
故 arr2[0]~arr2[k-1]==arr2[ j- k]~arr2[ j-1](3)
if arr2[k]=arr2[ j ]
则 arr2[0]~arr2[k-1]+ar2[k]==arr2[ j- k]~arr2[ j-1]+arr2[ j ]
即 arr2[0]~arr2[k]==arr2[ j- k]~arr2[ j](4)
(3)可得next[i].所以在arr2[k]=arr2[ j ]条件下,(4)得next[ j+1]=k+1 ((3)和(4)对比一下即可)
if arr2[k]!=arr2[ j ]
让k充当j继续回退!直到找到一个arr2[k]=arr2[ j ]!然后next[i+1]=k+1
只要分析一下无论是哪个回退点,都有arr2[0]~arr2[k-1]==arr2[start]~arr2[ j-1], arr2[0]~arr2[k-1]是哪个回退点产生并不重要,重要的是只要找到arr2[k]=arr2[ j ],我们就可以next[i+1]=k+1.
如果回退到0位还没有arr2[k]=arr2[ j ]怎么办?
next[0]=-1;
然后直接next[ j+1]=k+1=0;(也许是归纳法,哪位大佬解释一下-_-)
准备完毕,上代码:
OVER!