题目
Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
Example:
Input: haystack = “hello”, needle = “ll”
Output: 2
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().
我的想法
方法一:双指针
这双指针基本等于brute force了,最坏情况的复杂度O(m*n)
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0) return 0;
if(haystack.length() == 0) return -1;
int i = 0;
while(i < haystack.length()){
if(haystack.charAt(i) != needle.charAt(0)) {i++; continue;}
else{
boolean flag = true;
for(int m=i,n=0; n<needle.length(); m++,n++){
if(m >= haystack.length() || haystack.charAt(m) != needle.charAt(n)){
flag = false; break;
}
}
if(flag) return i;
else {i++; continue;}
}
}
return -1;
}
}
2019.9.14update:以前的双指针写的太蠢了。。。其实一个循环就足够了。
这次做的时候有好几个方面没有考虑到:
- 只考虑到了source的异常值,而忽略了target也存在异常值判断
- 理所因当的觉得不会有重叠的部分,即认为都是
“abcdcb”“dcb”
这样,不符合直接往后移就好了。忽略了"mississippi" "issip"
这种需要退回的情况 - 在写退回的时候走了弯路,直接在外面多加一层外层循环。这样既费时间,又忽略了题目要求的是返回第一次出现的index。在
"aabaabbabaaab" "abaa"
这种情况下,会返回j = 0
时的index = 7
,而实际上题目要的是index = 1
for(int j = 0; j < source.length(); j++){
int end = 0;
for(int i = j; i < source.length(); i++) {
if(source.charAt(i) != target.charAt(end)) {
end = 0;
continue;
}
end++;
if(end == target.length()) return i - end + 1;
}
}
因为前面的字符已经判断过,所以只需要退回到已判断的target的长度即可
最后Accept的写法
class Solution {
public int strStr(String source, String target) {
if(source == null || source.length() < target.length()) return -1;
if(target == null || target.length() == 0) return 0;
int end = 0;
for(int i = 0; i < source.length(); i++) {
if(source.charAt(i) != target.charAt(end)) {
i = i - end; //退回
end = 0;
continue;
}
end++;
if(end == target.length()) return i - end + 1;
}
return (source.length() == 0) ? 0 : -1;
}
}
方法二:队列
性能提升了很多,不知道equals()
的复杂度要不要算进去,如果不算的话复杂度就是线性了
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0) return 0;
if(haystack.length() == 0 || haystack.length()<needle.length()) return -1;
LinkedList<Character> listN = new LinkedList<>();
LinkedList<Character> listH = new LinkedList<>();
int lengthN = needle.length();
for(int i = 0; i < lengthN; i++){
listN.add(needle.charAt(i));
listH.add(haystack.charAt(i));
}
if(listH.equals(listN)) return 0;
for(int i = 1; i < haystack.length()-lengthN+1; i++){
listH.pollFirst();
listH.addLast(haystack.charAt(i+lengthN-1));
if(listH.equals(listN)) return i;
}
return -1;
}
}
解答
leetcode solution 1:substring
思想和用队列是一样的,但是这里直接用的substring函数,没有额外用数据结构了
2019.9.14update:这个方法很妙!!target的长度和内容是固定的,只要循环开头提取子串,再进行判断即可
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() == 0) {
return 0;
}
for(int i = 0; i < haystack.length() - needle.length() + 1; i++) {
if(haystack.substring(i, i + needle.length()).equals(needle)) {
return i;
}
}
return -1;
}
}
2019.9.16update:KMP实用性不高,这里需要记住的是另一个算法Rabin Karp Algorithm
这个算法通过比较字符串的Hash值来初步判断两字符串是否相等
- 计算target的Hash值:
"abbc" = a*31^3 + b*31^2 + b*31^1 + c*31^0
- 滚动计算source子串的Hash值:
设"anbcdd"中"anbc"的hash值为x,计算下一子串"nbcd"时无需重新计算,只 需去掉a的值加入d即可"nbcd" = (x - a*31^3)*31 + d*31^0
class Solution {
public int strStr(String source, String target) {
if(source == null || target == null) return -1;
if(target.length() == 0) return 0;
int BASE = 1000000;
int len = target.length();
//power
int power = 1;
for(int i = 0; i < len; i++) {
power = (power * 31) % BASE;
}
//target
int targetCode = 0;
for(int i = 0; i < len; i++) {
targetCode = (targetCode * 31 + target.charAt(i)) % BASE;
}
//source
int sourceCode = 0;
for(int i = 0; i < source.length(); i++) {
sourceCode = (sourceCode * 31 + source.charAt(i)) % BASE;
if(i < len - 1) {
continue;
}
//abc de --> a bcd e
if(i >= len) {
sourceCode = sourceCode - source.charAt(i - len) * power % BASE;
if(sourceCode < 0) {
sourceCode += BASE;
}
}
if(sourceCode == targetCode) {
if(source.substring(i - len + 1, i + 1).equals(target))
return i - len + 1;
}
}
return -1;
}
}
leetcode solution 2:KMP
KMP算法介绍
class Solution {
public int strStr(String haystack, String needle) {
if (haystack == null || needle == null || haystack.length() < needle.length())
return -1;
else if (needle.length() == 0)
return 0;
else
return KMP(haystack.toCharArray(), needle.toCharArray());
}
public int KMP(char[] source, char[] target) {
int[] next = next(target);
int matchLength = 0;
int sourceStart = 0;
// i: start index of source in the match
// j: start index of target in the match
int i = 0, j = 0;
while (sourceStart <= source.length - target.length) {
while (i < source.length && j < target.length && source[i] == target[j]) {
matchLength++;
i++;
j++;
}
if (matchLength == target.length)
return i - target.length;
else if (matchLength == 0 || matchLength == 1){
sourceStart++;
i = sourceStart;
j = 0;
matchLength = 0;
} else {
sourceStart = i - next[j - 1];
matchLength = next[j - 1];
j = next[j - 1];
}
}
return -1;
}
/*
* Build next array
*/
public int[] next(char[] target) {
int[] next = new int[target.length];
for (int i = 1; i < next.length; i++) {
if (target[i] == target[next[i - 1]])
next[i] = next[i - 1] + 1;
else if (target[i] == target[0]) {
for (int k = 0; k < i; k ++) {
if (target[k] == target[i - k]) {
next[i]++;
} else
break;
}
}
else
next[i] = 0;
}
return next;
}
}