28. Implement strStr()
Implement strStr().
Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
Example 1:
Input: haystack = “hello”, needle = “ll”
Output: 2
Example 2:
Input: haystack = “aaaaa”, needle = “bba”
Output: -1
Clarification:
What should we return when needle is an empty string? This is a great question to ask during an interview.
For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C’s strstr() and Java’s indexOf().
solution 1: substring
利用java自带的String的substring method.
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
int flag = 1;
for (int i = 0;i < haystack.length()-needle.length() + 1; i++) {
if (haystack.substring(i,i+needle.length()).equals(needle))
return i;
}
return -1;
}
solution 2: KMP substring algorithm
KMP算法可以在
O
(
n
)
O(n)
O(n)时间内完成substring的查找
其主要思想就是利用brute force方法中一个字符一个字符匹配时忽略的信息。
首先一个重要的概念就是prefix和suffix(前缀,后缀)以及前后缀数组。
前后缀数组table[i]是指代表substring中从起始到index i,前缀和后缀相同的最大长度。以substring needle = “d s a t d s d t”为例子,needle就是需要匹配的substring。
index = 0 1 2 3 4 5 6 7
needle = [d s a t d s d t]
table = [0 0 0 0 1 2 1 0]
在index为4的地方出现第一个前后缀相同的,‘d’,所以最大长度为1;
在index为5的地方相同前后缀都是"ds",所以最大长度为2;
在index为6的地方相同前后缀都是"d",所以最大长度为1;
计算前后缀数组的代码如下图所示。
int[] table = new int[needle.length()];
for (int i = 0; i < table.length;i++) {
table[i] = 0;
}
int i = 0, j = 1;
while (j < needle.length()) {
if (needle.charAt(j) == needle.charAt(i)) {
table[j] = table[j-1] + 1;
i++;
} else if (needle.charAt(j) == needle.charAt(0)) {
table[j] = 1;
i = 1;
} else table[j] = 0;
j++;
}
接下来讨论这个前后缀数组table究竟有什么作用:
同样以needle = “d s a t d s d t”为例子,假设匹配时,匹配到最后一个d时,发现和string不相同
....dsatdsw.....
needle : dsatdsdt
此时我们就得到了下面的信息:dsatds匹配到了且前后缀数组中’s’对应的值为2,说明最大相同前后缀长度为2。那么我i们此时就没有必要从string中d下一个字符开始匹配,我们只需要从不匹配的位置’w’处开始往后匹配,因为w前面的
"ds"一定可以和needle的前缀匹配,由于needle中前后缀相同的性质,所以只需要从string中的‘w’与needle中index为2的字符比较就可以,这个"2"就来自于前后缀数组table。
一个更直观的例子
A=”abaabaabbabaaabaabbabaab”
B=”abaabbabaab”
table = [0 0 1 1 2 0 1 2 3 4 5]
最开始匹配时发现到index = 5时匹配不上,于是回到table查table[5-1] = 2
A=”abaabaabbabaaabaabbabaab”
B=”abaabbabaab”
进行下一次匹配
A=abaabaabbabaaabaabbabaab
\\\\\\B=abaabbabaab
public int strStr2(String haystack, String needle) {
if (needle.length() == 0) return 0;
// first build the suffix-prefix table of the needle
// define two pointers
// table[i] represents the maximum length of
// the suffix that is also a prefix before i.
// for example, needle = [d s a t d s d t]
// table = [0 0 0 0 1 2 1 0]
int[] table = new int[needle.length()];
for (int i = 0; i < table.length;i++) {
table[i] = 0;
}
int i = 0, j = 1;
while (j < needle.length()) {
if (needle.charAt(j) == needle.charAt(i)) {
table[j] = table[j-1] + 1;
i++;
} else if (needle.charAt(j) == needle.charAt(0)) {
table[j] = 1;
i = 1;
} else table[j] = 0;
j++;
}
int t = 0;
int start = 0;
int k = 0;
while (k < haystack.length()) {
if (haystack.charAt(k) == needle.charAt(t)) {
t++;
k++;
} else if (t > 0) {
start = k - table[t-1];
t = table[t-1];
} else start = ++k;
if (t == needle.length()) return start;
}
return -1;
}
上面的代码中需要注意的是如何更新pointer t,k以及start。还有一点要注意的是while loop中需要特别解决t=0的情况,此时就需要pointer 看向后一位,start update为k,然后从头开始匹配,不可以使用table数组。