![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/15162a1H-56.png)
文章插图
![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/15162a954-57.png)
文章插图
相当于再一次把变量 j 退回到了next[ j ] , 也就是 j = 0 的局面 。
![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/1516291X2-58.png)
文章插图
情况仍然是 pat [ j ] != pat[ i-1 ] , 即 ‘G’ != ‘C’ 。j 已经不能再回溯了 , 所以得出结论:i=6时 , j=0 , next[ 6 ] = 0 。
小结:
1. 对模式串预处理 , 生成next数组
1.1 从next[ 2 ]开始 , 利用动态规划 + 双指针推推出跳转数组 。
2. 进入主循环 , 遍历主串
2.1. 比较主串和模式串的字符 。
2.2. 如果发现坏字符 , 查询next数组 , 得到匹配前缀所对应的最长可匹配前缀子串 , 移动模式串到对应位置 。
2.3.如果当前字符匹配 , 继续循环 。
![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/1516296012-59.png)
文章插图
【注:有关 Line 88 行的分析 , 仅为个人推断 , 可能错在错误或解释不清的地方 , 望各位有想法的大佬及学者留言评论 , 谢谢!】
- Line 88:为什么要加这一句呢?如果是找第一次匹配的位置 , 那直接 return 结果就好 。但是 , 我们需要找到模式串在主串中所有出现过的位置 。这就使得我们需要保证 , 在找到一次匹配后 , 需要滑动到某一特定位置 , 再次开始匹配 , 且滑动后的位置需要保证不会漏掉任何一种情况 。
(1)越界问题 。进入到这个 if 内部的前提是 j == m , 而 next 数组的最大索引位 m – 1 。因此 , 为了防止越界 , 传入 next 中的值应当在 [0, m – 1] 之内 。
(2)合理转化 。
![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/15162aN1-60.png)
文章插图
这是当前的状态 , 我们可以将当前 j 的位置补一个空白字符 , 其不与任何一个字符相匹配 , 因此需要回溯 。那么对于当前前缀子串 “ABA#” 其不存在对应的可匹配前后缀子串 , 根据上面的规则 , 需要在之前找到一组可匹配的前后缀子串 , 重新进行判断 。而如何滑动到之前的可匹配子串呢?我们可以把 j 的前一位视作当前的比较位置 , 假设该位置的字符是不匹配的 , 这样就可以直接回溯到之前的可匹配子串的下一次比较位置 。因此 , 此处位 j = next[ j – 1 ] 。
复杂度分析:
现在 , 该算法第一次计算t的哈希值时 , 时间复杂度为O(n);后续计算哈希值因为利用了滑窗的优化只有O(1);比较字符串的Equals()方法 , 一般地 , 可以视为O(1) 。
- 总时间复杂度为O( m + n ) , 其中计算 next 数组的时间为 O( m );匹配时 时间为 O( n ) 。
- 空间复杂度为O( m ) , 使用了一个 next 数组 , 其中 m 为模式串的长度 。
![.NET源码学习 [算法2-数组与字符串的查找与匹配]](http://shimg.jingyanzongjie.com/230724/1516294344-61.png)
文章插图
经验总结扩展阅读
- 【前端必会】不知道webpack插件? webpack插件源码分析BannerPlugin
- 如何开展学习十八大,提高了教育的质量
- 十岁小孩学习不太好跟家长又不会怎么教他呢
- 20220929-ArrayList扩容机制源码分析
- 高效上课学习方法
- 怎么做一个学习的人
- Optional源码解析与实践
- Go 源码解读|如何用好 errors 库的 errors.Is 与 errors.As() 方法
- .Net下的分布式唯一ID
- .NET 反向代理 YARP 代理 GRPC
