这道题有多种解法,先上最简单的解法,然后上稍复杂的解法。个人习惯上来就给复杂的展示,直接就把我劝退了。。
第一种:内置函数
class Solution {
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
}
第二种:双指针法
class Solution {
public int strStr(String haystack, String needle) {
//根据题意,needle空串直接返回0即可
if (needle == "") return 0;
int i = 0, j = 0;
while (i < needle.length() && j < haystack.length()){
char c = needle.charAt(i);
char ch = haystack.charAt(j);
if (c == ch){
//匹配同时往后移动
i++;
j++;
}else {
//else是出现不匹配时的处理
//j - i 就表示回到第一个匹配的字符的下标位置
// 然后 + 1 表示从第一个匹配的字符的下一个再开始重新匹配。
//例如 : haystack = "abd" needle = "abe" 当d和e不匹配时回到第一个匹配的a的位置,+1,从b开始重新匹配。
j = j - i + 1;
i = 0;
}
//返回第一个匹配的位置下标。
if (i == needle.length()){
return j - i;
}
}
//没有匹配的返回-1
return -1;
}
}
第三种:kmp算法
观察下面的next数组的构造实现和kmp匹配方法的实现基本是相同的。
j :代表前缀的最后一个字符,同时还代表前后缀子串的最长匹配长度。
i : 代表后缀的最后一个字符。
当发生不匹配时,发生在后缀的后面,就要跳跃到前缀的后面,重新开始匹配。
需要理解好多概念:
啥是最长公共前后缀?
为啥只判断s【i】 和 s【j】 就能求出最长公共前后缀长度呢??
可能还有的会问为啥根据j = next【i】 来跳跃j呢??
借鉴Carl的视频理解,字面讲解很难理解 :https://www.bilibili.com/video/BV1PD4y1o7nd/?spm_id_from=333.788
class Solution {
public int strStr(String haystack, String needle) {
if(needle.equals("")){
return 0;
}
int[] next = new int[needle.length()];
getNext(next, needle);
int j = 0;
for(int i = 0; i < haystack.length(); ++i){
while(j > 0 && haystack.charAt(i) != needle.charAt(j)){ //不匹配时j根据next表向前移动。
j = next[j - 1];
}
if(haystack.charAt(i) == needle.charAt(j)){
j ++; //匹配就同时往后移动指针,这里只移动j即可, 因为i在for循环中移动
}
if(j == needle.length()){
return i - needle.length() + 1; //匹配的起始位置是i - needle.length() + 1. 可以自己举个例子看看确实是这样的。
}
}
return -1;
}
public void getNext(int[] next, String needle){
//这里使用不减一的操作。
//还有使用统一减一的方法的,为什么使用统一减一的写法呢???
//有什么好处,我不知道,没弄清楚
int j = 0; //第一个字符的最长匹配长度肯定是0, 也是起始匹配位置
next[0] = j;
for(int i = 1; i < needle.length(); ++i){
while(j >= 1 && needle.charAt(i) != needle.charAt(j)){ //不匹配时,根据前缀表跳跃
j = next[j - 1];
}
if(needle.charAt(i) == needle.charAt(j)){
j ++; //匹配就同时往后移动
}
next[i] = j;
}
}
}