最近学习了kmp算法,看了b站几个讲解视频,感觉最后还是差点意思,最后还是自己在草稿本上涂涂画画
做了多次实验才总结出了道理,在此记录下来,以便以后复习
结论写在前:
当最大公共前后缀匹配不上的时候,我们需要去匹配第二大公共前后缀,还匹配不上?匹配第三大前后缀,如此迭代。
1。建议结合b站视频up主“正月点灯笼”的视频学习,思路非常清晰,动态规划就是和他学的!视频连接(20分钟左右)
2.最长公共前缀表
比如字符串:ABABAB
有前缀
A
AB
ABA
ABAB
ABABA
这五个前缀,都有对应的最长公共前后缀(结合视频)
得到
(1位)A—>0
(2位AB—>0
(3位ABA—>1
(4位ABAB—>2
(5位ABABA—>3
再在前面补上一个: 0—>-1 的映射关系,我们得到了一个数组 [-1,0,0,1,2,3],前缀长度为数组下标,最长公共前后缀为对应下标的值。(前缀匹配表)
关于最长公共前后缀:比如一个字符串“ABABA“最长公共前后缀是3
那我们可以得到如下关系
图1
字符串长度-最长公共前后缀长度=5-3=2 ,可以理解为把字符串向后移动两位
现在我们代入一个场景
图2
想找到字符串1中字符串2第一次出现的位置
我们可以开始比较,很明显第六位C和B没匹配上,但是前面五位都匹配上了!
根据前面得到的数组 arr=[-1,0,0,1,2,3],arr[5]=3,最长公共前后缀=3,
那我们可以这样做,把字符串2索引=3的字符再去和字符串1中的C比较,
图3(注意图中圆圈前面的公共前缀ABA)
如果还是匹配不上?arr[3]=1,把字符串2索引=1的字符再去和字符串1中的C比较,
图4(注意图中圆圈前面的公共前缀A)
如果还是匹配不上?arr[1]=0,把字符串2索引=0的字符再去和字符串1中的C比较,
图5
如果还是匹配不上?arr[0]=-1,把字符串2再向前移动一位,可以理解成跳过C,不和C进行比较
图6
然后从上图位置开始继续比较(结合视频)
为什么可以这样做???
为什么我们图3可以直接把字符2移动两位??
观察图2,图3 和图1
在已知ABABA完全匹配的情况下 ,我们对ABABA得到了一个最大公共前后缀=ABA,那么意味着,把字符串从最大前缀的位置移动到最大后缀的位置,移动5-3=2,从而跳过了前两位AB是完全没问题的
整理一下:
已知前缀ABABA完全匹配 ,想验证第六位C和B是否匹配,得到不匹配,我们只能退而求其次,验证第二大公共前缀AB的第三位是否匹配,还不匹配验证第三大公共前缀A的下一位是否匹配,还不匹配,跳过C!
str有最大公共前后缀 common1, 第二大公共前后缀 common2
条件一:common2必然小于等于common1
条件二:str中必然会有两个common1(左ommon1,右common1)
条件三:左common2起始于左common1的头部,右common2结束于右common2的尾部
根据条件1,2 ,3我们可以推算出 common2就是common1的最大公共前后缀(画图)
第n+1大公共前后缀=第n大公共前后缀的最大公共前后缀。
相比于传统的暴力匹配逐位跳过比较,这种KMP根据前缀匹配表来跳过显得更加高效。(建议草稿纸自己再试一遍)