递归思想与KMP算法
很有幸读到了很实用优秀的一篇关于kmp算法的解释博客通俗易懂的字符串匹配KMP算法及求next值算法,在写博客前推荐给大家。
接下来是我自己对递归相关知识的理解和总结
迭代和递归
迭代是适合对数据掌握很多的,知道有几层。递归是适合掌握很少信息的。现实问题中能用迭代就不要用递归,因为效率很低。迭代时循环结构,而递归是选择结构。
斐波那契数列的递归实现
以兔子的繁衍为例:
递归定义:一个直接调用自己或者间接调用自己的函数,叫做递归函数。每一个递归必须至少有一个条件,当满足这个条件时,递归结束,不然就会陷入永无止尽的循环递归。
递归可以使程序结构更清晰,减少读懂程序的时间。但是大量的递归会建立很多副本,很消耗内存和时间
递归有调用和回退两个阶段,回退是调用的逆序。递归是一个过程,每一次调用过程都是一样的。如:算n!
分治思想:把问题分解成一个个小模块,每个模块都是类似的问题,然后一个个解决每个模块的问题。
实际应用
汉诺塔 借助中间的柱子实现
八皇后问题每次都是寻找安全区域的新皇后,在下一个安全区域再寻找新皇后
字符串
串string:零个或者多个字符组成的有限序列。字串和主串,over是lover的字串。
字符串的比较大小:字符串里每一个字符的ASCII码的大小
字符串的存储结构:顺序和链式。因为不同的字符串一般是连在一起表述的,不能断章取义,所以我们习惯用顺序,定义一段足够长的存储去来存储。
BF算法和KMP算法
BF:朴素的模式匹配算法。从开头一个一个匹配。很暴力但是效率低下。
KMP
直接上图
第0号元素存放字符串的长度,所以1表示第一个元素。我们发现,i5不等于j5。就会回溯到下图位置
回溯法思维:在哪里跌倒就从哪里爬起来,思想好效率低。
KMP思维:省去不必要的回溯,问题应该由模式串(下面那一串)决定,而不是目标串。
这里有S和T串,同样在i5出现失配。需要回溯得是j,而i是不需要的。所以直接用j1, 回溯到1,对应到失配的地方i5,重新开始匹配
接下来是特殊情况
情况一
因为i1=i2,在i3失配,则j只需要回溯到2
情况二
因为 i1,i2=i4,i5 j1,j2=i1.i2 所以 j1,j2=i4,i5
为了不错过任何有可能的匹配,j需要回溯到2的后一位,和失配处匹配
情况三
j 向右移一个,因为不知道之后会有什么。
基于以上情况的总结:
如果前面无重复,j从1开始回溯
如果前面没重复,T串1个一样,j从2开始回溯,T串2个一样,j从3开始回溯,T串有3个相等,j从4开始。
next数组
现在给模式匹配串 T串 添加一个k数组,也就是next数组。用来指导模式匹配串下一步应该改用第几号元素去匹配。
还是那几个列子动图演示视频
重点搞清楚什么是前缀和后缀
前缀就是第一个元素往后,后缀司最后一个元素往前,有重复元素时应该特殊处理,举例:
S abaaabb
T abaaaba *此处失配,前缀为ab,后缀也是ab,有2个重复,k=3
S abaaabb
T ababbaa*此处失配,前缀是a,后缀也是a,有1个重复,k=2
S abaaabb
T abcabcb*此处失配,前缀是a,后缀是b,没有重复,k=1
S ssssst
T sssstt*此处失配,前缀为sss,后缀也是sss,有3个重复,k=4
应试捷径
现在我们发现一个问题:
我们不用去管目标串 S串 具体有哪一些元素,要判断k的值,只需要看模式串 T串 在失配项之前的元素有多少是重复的就可以了,代码只需要知道下一步要走到哪里。
KMP代码实现教学后缀是相对的,前缀是固定的
KMP是有缺陷的,比如:
S aaaabce
T aaaax
很容易得到next是 01234
但是元素在第四个a处失配,没有必要回溯到123,效率很低。
改进:如果T失配元素和T前缀元素相等,那就应该回溯到前缀元素的next值上面去,因为中间的那一段都是相等的,就直接回到前缀元素,他的next值什么就指向下一个对应的j。
KMP并不是最优秀的字符串匹配算法