想写一个算法思路(意图)的系列,从KMP算法开始吧,不讲代码,只讲”为什么要这么想,这么写“,看网上这么讲的还没见到,自己想写的还是必须自己写啊~
KMP一直看书不明白的,看视频真的有帮助,老师会自然而然地说出思路而非书上那般生硬。
B站,严蔚敏奶奶视频教程
一、kmp的目标:
1,防止i指针回溯。
需要重新匹配时,其实前面的已经匹配成功,问题可以转化为模式串的自比较,从而省去i指针的回溯。
2,让j指针回溯得尽量少。
模式串的前缀、后缀的最长重复位数可以代表需要跳过几位。
二、一个实例
一位一位比较时
遇到主串i指针的字符 与 模式串j指针的字符 不匹配时。
-------i
abaabcabbbbb
abaabcac
-------j
发生了i指针回溯,同时产生了已经成功匹配序列的错位
-i
abaabcabbbbb
-abaabcac
-j
再一位一位比较……
--i
abaabcabbbbb
--abaabcac
--j
---i
abaabcabbbbb
--abaabcac
---j
突破点是已经成功匹配的字符串,错位后,除了一些特殊情况,必然不能匹配!
那个“特殊情况”就是前缀与后缀的重复,同时,匹配成功的主串、模式串的部分是相等的! 这样next数组仅仅交给模式串就等于主串、模式串都遵守了!
——回到第一步那里,接着可以直接进行下一步:j回溯到模式串的b(从第一步的c)。
-------i
abaabcabbbbb
------abaabcac
-------j
三、原理要点
0,明白什么不用比较,什么还需比较
1,基于已经成功匹配的字符串(已知匹配成功的位数)
2,i回溯会导致已经匹配成功的串错位(主串与模式串),如何防止i指针回溯导致的浪费?——除了一些特殊情况,错位必然不能匹配!
3,特殊情况——只用考虑【已经匹配成功的部分主串(也就是部分模式串!匹配成功的部分代表它们完全一致!)】的错位是否会有”重复“
4,错位时,模式串前缀后缀的重复可以避免重新匹配(模式串与模式串——next数组)
5,next数组标记的有关于当前字符前的子串的前缀、后缀的最长重复长度,也就是可以不直接退回j串第一个字符的可以少回溯的重复部分。
四、通过观察就能解出next数组值的方法!!!
以下摘自:next数组两种求法
1)算出每一个字母前缀后缀的最大公共子串长度(下一步会把最后一位移走,所以最后一位可以不算)
P | a | b | a | b | a | c | a |
前后缀最大公共子串长度 | 0 | 0 | 1 | 2 | 3 | 0 | * |
关于前缀后缀放张图,下图引自:https://www.cnblogs.com/zhangtianq/p/5839909.html
2)最大公共子串长度整体向后移动一个长度,最前面的元素值填 -1,即为 next 数组的第一版本
P | a | b | a | b | a | c | a |
next 数组第一版 | -1 | 0 | 0 | 1 | 2 | 3 | 0 |
3)(如果你需要的 next 数组第一个值为 -1,第二步就是结果了)next 数组的每一个值分别+1,即求得 next 数组。
P | a | b | a | a | b | c | a |
next 数组第一版 | 0 | 1 | 1 | 2 | 3 | 4 | 1 |
五、理解next
next[j] 内的值不代表j之前的子串前后缀的最大长度,
而是“当模式串中第j个字符与主串相应字符‘失配’时,
在模式串中需要重新和主串中该字符进行比较的字符的位置”。
这就是第四节方法中求出前后缀以后,为什么还要进行后续的操作!
书上的算法不过就是如何实现第四节的方法罢了(但是教材可没告诉我那么简单的观察求next数组的方法啊!!)
书上的算法不过就是如何实现第四节的方法罢了(但是教材可没告诉我那么简单的观察求next数组的方法啊!!)
书上的算法不过就是如何实现第四节的方法罢了(但是教材可没告诉我那么简单的观察求next数组的方法啊!!)
教材定义图引自:https://blog.csdn.net/qq_37969433/article/details/82947411
*注:教材上的”1“是[其他情况],不是[”不匹配时“]
这图上的精确的数学语言看着就让人头疼,不如按着公式试试几个例子。
这是严蔚敏教材P81的next数组值
abaabcac
01122312
先用观察法求一遍验证一下(卧槽,竟然如此简单,太感谢那位博主了!链接就在第四节第一行里)
_abaabcac
_0011201*
-10011201
_01122312
验证公式:我们取第4位的”a“,则这个a的next[4]的值是”k=2“,[1到2-1]=[4-2+1到4-1]即【1到1】=【3到3】即 [a]=[a]。
这也应当是”最长前后缀重复长度“
abaabcac
---2----
验证公式:我们取第6位的”c“,则这个a的next[6]的值是”k=3“,[1到3-1]=[6-3+1到6-1]即【1到2】=【4到5】即[ab]=[ab]。
这也应当是”最长前后缀重复长度“
abaabcac
-----3--
下面是回溯j指针的实例。
【现在是f比a】同时,在匹配错误时跳过多少?
---i
abafbcabbbbb
abaabcac
---2----------------next
回溯至2【现在是f比b了】(j=next[j],就是j赋值当前位置的next数组的值,也就是下一步的位置(已经算好了跳到哪里))
---i
abafbcabbbbb
--abaabcac
---1-----------------next
回溯至1(回溯到头都没有,之前的整串又配不上,GG,真匹配不上了)
这里发现了又匹配到了f对a,这个涉及”对KMP算法的一种改进“,书上有。
---i
abafbcabbbbb
---abaabcac
---0------------------next