题目
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
来源:力扣(LeetCode)
链接:原题链接
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
KMP算法简介
算法背景
在一个长字符串中,查找是否有一字串出现。暴力匹配是这样的:从头开始匹配,出现不匹配的字符在从第二个字符开始看是否匹配。这样的时间复杂度随着是极高的。
kmp的思想
当出现不匹配的情况时,如何快速定位到下次匹配开始的位置,这很关键。kmp利用了前缀表来解决定位的问题
前缀表,后缀表
前缀表就是不包含最后一个字符的所有子字符串,例如:
对于字符串 “aaadaa”,它的所有前后缀字符串分别为:
前缀: 后缀:
a, a
aa, aa
daa, aaa
adaa, aaad
aadaa, aaada
next数组(数组元素值全做减 1 处理)
next数组的元素是匹配失败后,定位的位置下标。next数组的元素怎么来的呢?
假设当前的子字符串是 needle ,那么从前向后扫描needle的所有子字符串,比如“aaadaa”,
第一个子字符串为 a ,此时没有前后缀字符串,所以前后缀相等长度为 0,所以当前值就是数组的第一个数,设置为 0 - 1 = -1;
第二个子字符串为 aa,此时前缀字符串为 a ,后缀字符串也为 a ,且相等,相等的长度为1,那么存到数据就是 1 - 1 = 0;
以此类推,next数组存的元素就是前后缀相等的前缀长度,也等于后缀长度。所以这个next数组可以也叫做前缀数组。
算法原理
将要匹配的模式字符串转为next数组,由于next数组保存的是前后缀相等的模式字符串的子字符串的前缀长度(减 1 ),所以当模式字符串匹配到半路发生不匹配时,定位的操作就交给这个next数组,假设在目标字符串的下标为5的位置发生了不匹配,那就查next数组中下标为4的值,将匹配初始位置回退到这个值。这个原因就是,next数组存储了前后缀相等的前后缀长度,并且不匹配位置之前一定都是匹配的,那么就可以用当前子字符串的前缀取匹配当前的后缀位置,这是一定匹配的,而且可以快速跳过不必要的对比,时间复杂度为O(n + m)
代码
public class KMPController {
public static void main(String[] args) {
int str = strStr("hello", "ll");
System.out.println(str);//输出为2
}
public static void getNext(int[] next, String s) {
int j = -1;
next[0] = j;
for(int i = 1; i < s.length(); i++) {
//i和j的元素不等时,重置j的起点为next数组最新元素
if(j >= 0 && s.charAt(i) != s.charAt(j + 1)) {
j = next[j];
}
if(s.charAt(i) == s.charAt(j + 1)) {
j++;
}
next[i] = j;
}
}
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 = -1;
for (int i = 0; i < haystack.length(); i++) {
if(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;
}
}