问题描述
实现 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
示例3:
输入:haystack = "", needle = ""
输出:0
提示:
0 <= haystack.length, needle.length <= 5 * 104
haystack
和needle
仅由小写英文字符组成
问题思路
思路1:暴力法
直接让haystack所有长度为m的子串与needle进行匹配。如果匹配成功,则返回相应的下标,如果均无法匹配,则返回-1。
实现代码如下,空间复杂度O(1),时间复杂度O(m.n)。运行时间0 ms。
class Solution {
public int strStr(String haystack, String needle) {
int n = haystack.length();
int m = needle.length();
if (n < m) {
return -1;
} else if (n == 0) {
return 0;
}
for (int i = 0; i <= n - m; i++) {
if (haystack.substring(i, i + m).equals(needle)) {
return i;
}
}
return -1;
}
}
思路2:暴力法-优化
同样让所有子串匹配,区别是先进行一次首字符比较,如果一致,则继续匹配剩下的部分;如果首字符不一致,则直接尝试下一个子串。
这里直接放上 java.lang.StringLatin1.indexOf(byte[] value, byte[]str) 库函数的源码。
实现代码如下,空间复杂度O(1),时间复杂度O(m.n)。运行时间0 ms。
@HotSpotIntrinsicCandidate
public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
byte first = str[0];
int max = (valueCount - strCount);
for (int i = fromIndex; i <= max; i++) {
// Look for first character.
if (value[i] != first) {
while (++i <= max && value[i] != first);
}
// Found first character, now look at the rest of value
if (i <= max) {
int j = i + 1;
int end = j + strCount - 1;
for (int k = 1; j < end && value[j] == str[k]; j++, k++);
if (j == end) {
// Found whole string.
return i;
}
}
}
return -1;
}
思路3:Knuth-Morris-Pratt 算法(KMP算法)
由前两个思路我们可以发现,当原串在匹配时如果与匹配串第 i 个位置不一致,原串指针就要回退 i - 1 个单位,然后重新开始比较,这无疑会浪费大量的时间。
那么有没有一种算法可以在不一致时原串指针位置不回退,仍然从当前位置继续比较呢?
答案是肯定的,这就是KMP算法。该算法通过在发生不一致时,利用已匹配子串具有的相同前后缀的特点,减少回退的长度,加速下一次的匹配。
例如原串 abcabe 在 e 处不一致时,因为已匹配的字串“abcab”有相同的前后缀“ab”,只需将指针移到"ab"的下一个位置,然后从匹配串的 c 处、原串的 e 处继续比较即可。
该算法空间复杂度O(m),时间复杂度O(m+n)。