前言
kmp!!!!!!!!!!!
28. 实现 strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1: 输入: haystack = "hello", needle = "ll" 输出: 2
示例 2: 输入: haystack = "aaaaa", needle = "bba" 输出: -1
说明: 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。 对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
思路
多说无益,kmp搞起!
kmp算法就是用来解决字符串匹配问题的
一般来说当我们循环比较后碰到不同的字符后,搓移一位继续比较,可是这样的时间复杂度太长了,
既然当前比较的值不一样,说明之前的我们已经比较过了,那么我们可不可以根据比较过的数据的一些特征来减少比较呢?能!这就是kmp算法!
思路有了,具体是什么特征呢,因为我们要顺序比较,那么有没有一种特征是我之前比较过的一部分和我当前需要比较的部分相等呢?这就引入了前缀表: 根据前缀表遍历方式就成了下图
如何生成前缀表, 在理解前缀表之前我们先要了解前缀和后缀子串,如下图可以理解
而前缀表就是:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。 而前缀表的具体生成步骤可以看以下视频
KMP算法之求next数组代码讲解_哔哩哔哩_bilibili
总结就是根据前缀表的前缀和后缀相同这一特性来生成对应位置的前缀表的值。再根据生成的前缀表移动子串进行匹配。
代码
public int strStr(String haystack, String needle) {
if(needle.length()==0){
return 0;
}
int[] next = new int[needle.length()];
getNext(next, needle);
int j = -1;
for(int i = 0; i<haystack.length();i++){
while(j>=0 && haystack.charAt(i) != needle.charAt(j+1)){
j = next[j];
}
if(haystack.charAt(i)==needle.charAt(j+1)){
j++;
}
if(j==needle.length()-1){
return (i-needle.length()+1);
}
}
return -1;
}
public void getNext(int[] next, String s) {
int j = -1;
next[0] = j;
for (int i = 1; i < s.length(); i++) {
while (j >= 0 && s.charAt(i) != s.charAt(j + 1)) {
j=next[j];
}
if (s.charAt(i) == s.charAt(j + 1)) {
j++;
}
next[i]= j;
}
}
注意
因为next数组生成的方式有所不同,所以得出的next数组也有所不同,主要领会思想。
总结
今天的kmp可以说是至今为止最难的题目了,用了一上午的时间才理解,可以说是代码能看懂了,距离自己手撕还有一定距离。