暴力解法
核心思想: 对haystack字符串从头开始循环,用res记录每趟循环的开始位置(即needle字符串在haystack字符串中出现的第一个位置),每趟循环均从头到尾对needle字符串中的字符依次扫描匹配,若匹配失败,则进行下一趟循环,若needle字符串中的字符全部匹配成功,则返回res。
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0)//特判
return 0;
for(int i = 0; i < haystack.length(); i++){
int res = i;//needle字符串在haystack字符串中出现的第一个位置
int j = 0;//依次匹配needle字符串
if(haystack.length() - i >= needle.length()){//剪枝优化
for(; j < needle.length(); j++){
if(haystack.charAt(i) == needle.charAt(j))//对应位置的字符相同,继续匹配
i++;
else//对应位置的字符不相同,结束本趟匹配循环
break;
}
}
if(j == needle.length())//匹配成功
return res;
i = res;//下一趟循环
}
return -1;//匹配失败
}
}
KMP算法
核心思想: 由于暴力解法的匹配过程中存在无效的重复匹配,KMP算法通过分析needle字符串的前缀和后缀相同的最大长度(可能为0),得到数组next[],数组中每个位置的值就是该下标应该跳转的目标位置,也是needle字符串截止到该下标字符之前的子字符串的前缀和后缀相同的最大长度,从而减少重复匹配。
注意: 快速生成数组next[]是KMP算法的关键
详细说明: next[j]=k表示截止到字符needle[j]之前的子字符串needle[0-j-1]的前缀和后缀相同的最大长度为k,那么求next[j+1]则有两种情况:(1)needle[j]=needle[k],此时next[j+1]=k+1;(2)needle[j]!=needle[k],此时需看是否存在长度更小的前缀和后缀相同的最大长度next[k],若存在,则next[j+1]=next[k]+1,若不存在,则说明截止到字符needle[j+1]之前的子字符串needle[0-j]不存在相同的前缀和后缀,next[j+1]=0。
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0)//特判
return 0;
//快速生成数组next[]
int[] next = new int[needle.length()];
next[0] = -1;
int j = 0, k = -1;
while(j < needle.length() - 1){//与next[j+1]配合
if(k == -1 || needle.charAt(j) == needle.charAt(k)){//不存在相同的前缀和后缀或者needle[j]=needle[k]
j++;
k++;
next[j] = k;//next[j+1]=k+1或者next[j+1]=0
//next[++j] = ++k;
}
else
k = next[k];//寻找长度更小的前缀和后缀相同的最大长度
}
//利用数组next[]进行匹配
int i = 0;
j = 0;
while(i < haystack.length() && j < needle.length()){
if(j == -1 || haystack.charAt(i) == needle.charAt(j)){//needle从头开始匹配或者对应位置的字符相同
i++;
j++;
}
else//对应位置的字符不相同,needle指针j回退并继续匹配
j = next[j];
}
if(j == needle.length())//匹配成功
return i - needle.length();
else//匹配失败
return -1;
}
}