题目
https://leetcode-cn.com/problems/implement-strstr/
暴力解法
我们加上flag来进行判断
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
for (int i = 0; i <= n-m ; i++) {
boolean flag = true;
for (int j = 0; j < m; j++) {
if (haystack.charAt(i + j) != needle.charAt(j)) {
flag = false;
break;
}
}
if (flag) {
return i;
}
}
return -1;
}
或者不想用flag进行判断,时间复杂度还会降低的方式
public int strStr(String ss, String pp) {
int n = ss.length(), m = pp.length();
char[] s = ss.toCharArray(), p = pp.toCharArray();
// 枚举原串的「发起点」
for (int i = 0; i <= n - m; i++) {
// 从原串的「发起点」和匹配串的「首位」开始,尝试匹配
int a = i, b = 0;
while (b < m && s[a] == p[b]) {
a++;
b++;
}
// 如果能够完全匹配,返回原串的「发起点」下标
if (b == m) return i;
}
return -1;
}
KMP
作者:海纳
链接:https://www.zhihu.com/question/21923021/answer/281346746
在此先说明为什么会快
-
因为 KMP 利用已匹配部分中相同的「前缀」和「后缀」来加速下一次的匹配。也就是说,找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。
-
因为 KMP 的原串指针不会进行回溯(没有朴素匹配中回到下一个「发起点」的过程)。随着匹配过程的进行,原串指针的不断右移,我们本质上是在不断地在否决一些「不可能」的方案。当我们的原串指针从 i 位置后移到 j 位置,不仅仅代表着「原串」下标范围为 [i,j) 的字符与「匹配串」匹配或者不匹配,更是在否决那些以「原串」下标范围为 [i,j) 为「匹配发起点」的子集。
下面画图展示与代码息息相关的方面
构建next数组
为什么回溯是回到 j = next[j-1]
next[j-1]就是记录着j(包括j)之前的子串的相同前后缀的长度。
为什么相等时要同时向后移动i和j
同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。
用next数组完成要求
代码实现
public static int strStr(String haystack, String needle) {
if (needle.length() == 0) {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-1];}
if (haystack.charAt(i)==needle.charAt(j)){j++;}
//因为j是在i循环里面++,所以此时的j是等于needle的长度的
if (j == needle.length()){return i-j+1;}
}
return -1;
}
public static void getNext ( int[] next, String s){
//初始化
int j=0;
next[0]=j;
for (int i = 1; i < s.length(); i++) {
//无论相同或者不同,最终都要给next[i]赋值
//前后缀不同
while(j > 0 && s.charAt(i)!=s.charAt(j) ){
j=next[j-1];
}
//前后缀相同
if(s.charAt(i)==s.charAt(j)){ j++;}
next[i] = j;
两个疑问点:
- 为什么不相同在相同的前面
因为处理不同时,涉及到回退操作,若s的i和j相等我们还需给next的i赋值上j+1
- 为什么不能用if来代替while
在下面这个例子中,将得不到正确答案
“ababaabbbbababbaabaaabaabbaaaabbabaabbbbbbabbaabbabbbabbbbbaaabaababbbaabbbabbbaabbbbaaabbababbabbbabaaabbaabbabababbbaaaaaaababbabaababaabbbbaaabbbabb”
“abbabbbabaa”
意味着我们在回退j的时候,一次有时候是不够的。