力扣解题思路分享-----KMP算法
但是根据next表的定义可以知道,前缀ab与后缀ab在第一步已经证明了是相同的, 同时前缀也已经证明了和T的前(next=2)的相同,那么前缀(ab)=后缀(ab)=T的前(next=2)个字符即第二步执行的前两个字符判断已经判定相同了,所以执行红色字体代表的朴素法又会浪费时间,因此,我们直接把S的指针i和T的指针j跳过后缀ab,此时发现指针i=5(第一步执行结束后i的位置,因此,指针i其实一直向
力扣题目: 28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
关于这道题目最容易想到的是暴力解法,时间复杂度是O((m-n)*n) , 但是KMP算法能达到O(m+n)
而对于 KMP算法,我想在这分享一下理解的思路(参考大佬的博文 : 图解KMP算法,带你彻底吃透KMP-CSDN博客)。
1.如果只是想使用KMP算法,不想了解原理,大概的使用思路是:
(1)先得到needle字符串的next数组 。
next数组是什么呢?在这之前需要普及下前缀后缀两个概念:前缀是指一个不含总字符串(eg : "sdfwffe")最后一个字符(eg: "e")但必须以第一个字符开头的子字符串(很多个);同理,后缀是不含第一个字符串但必须以最后一个字符结束的子字符串。
由此,可以给next数组下定义是: 总字符串的所有子字符串的最大相等前缀和后缀的长度所组成的表,如下例子所示。
图1
(2)得到next数组x后,开始采用双指针(主字符串设为S ,它的指针设为i , 目标字符串设为T,它的指针设为j), 让T和S先按照朴素解决法进行匹配,如图2所示,当指针i=j为某位置不匹配(i,j位置的字符不相同)时,指针i位置不移动,通过指针j的值锁定next表中的next值,然后将指针移动到next位置上(即 j=next),循环往复,得到结果。
图2
2.如果想深入了解KMP算法的基础原理,考虑从朴素法的过程进行分析,逐步分解这个过程。
观察图2,第一步第二步第三步,你会发现T的首字符是a,T的第二第三字符都不是a, 但是第一步匹配时已经证明了S的第二字符第三字符和T的相同,即不可能是a , 所以朴素法的第二步第三步是浪费的,因此证明了指针i不应该在第二轮循环时回到S的首位再前进一格(即i=1),应该直接到i=3 ; 对于目标字符串T的指针j,朴素法中都是在第二轮回到首位(j=0), 但是仔细查看,题目中的例子T,你会发现在第一步中指针已经移动到了j=5 , 在这之前的字符串是 abcab,查看next表可知最大相等前缀与后缀都是 ab , 此时如果还按照朴素法,使得j=0 , 那么在第二轮对比的两个字符是(S(i=3) 和T(j=0)),但是根据next表的定义可以知道,前缀ab与后缀ab在第一步已经证明了是相同的, 同时前缀也已经证明了和T的前(next=2)的相同,那么前缀(ab)=后缀(ab)=T的前(next=2)个字符即第二步执行的前两个字符判断已经判定相同了,所以执行红色字体代表的朴素法又会浪费时间,因此,我们直接把S的指针i和T的指针j跳过后缀ab,此时发现指针i=5(第一步执行结束后i的位置,因此,指针i其实一直向前,不用考虑回溯即可) , 指针j=0+2=2 即j=next=2 。 总结下来算法编写思路如上死记硬背算法解析所示,即i一直向前即可,不考虑回溯,j的话根据每一次匹配出的字符串得到next值,直接让j=next即可,举例过程如图3所示
图3
class Solution {
// KMP 算法
// ss: 原串(string) pp: 匹配串(pattern)
public int strStr(String ss, String pp) {
if (pp.isEmpty()) return 0;
// 分别读取原串和匹配串的长度
int n = ss.length(), m = pp.length();
// 原串和匹配串前面都加空格,使其下标从 1 开始
ss = " " + ss;
pp = " " + pp;
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
// 构建 next 数组,数组长度为匹配串的长度(next 数组是和匹配串相关的)
int[] next = new int[m + 1];
// 构造过程 i = 2,j = 0 开始,i 小于等于匹配串长度 【构造 i 从 2 开始】
for (int i = 2, j = 0; i <= m; i++) {
// 匹配不成功的话,j = next(j)
while (j > 0 && p[i] != p[j + 1]) j = next[j];
// 匹配成功的话,先让 j++
if (p[i] == p[j + 1]) j++;
// 更新 next[i],结束本次循环,i++
next[i] = j;
}
// 匹配过程,i = 1,j = 0 开始,i 小于等于原串长度 【匹配 i 从 1 开始】
for (int i = 1, j = 0; i <= n; i++) {
// 匹配不成功 j = next(j)
while (j > 0 && s[i] != p[j + 1]) j = next[j];
// 匹配成功的话,先让 j++,结束本次循环后 i++
if (s[i] == p[j + 1]) j++;
// 整一段匹配成功,直接返回下标
if (j == m) return i - m;
}
return -1;
}
}
作者:宫水三叶
链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solutions/575568/shua-chuan-lc-shuang-bai-po-su-jie-fa-km-tb86/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
更多推荐




所有评论(0)