Description
For a given source string and a target string, you should output the first index(from 0) of target string in source string.If target does not exist in source, just return -1
.
对于一个给定的 source 字符串和一个 target 字符串,你应该在 source 字符串中找出 target 字符串出现的第一个位置(从0开始)。如果不存在,则返回
-1
。
Do I need to implement KMP Algorithm in a real interview?
- Not necessary. When you meet this problem in a real interview, the interviewer may just want to test your basic implementation ability. But make sure you confirm with the interviewer first.
在面试中我是否需要实现KMP算法?
- 不需要,当这种问题出现在面试中时,面试官很可能只是想要测试一下你的基础应用能力。当然你需要先跟面试官确认清楚要怎么实现这个题。
Example
Example 1:
Input:source = "source" , target = "target"
Output:-1
Explanation:If the source does not contain the target content, renturn -1.
Example 2:
Input:source = "abcdabcdefg" , target = " bcd"
Output:1
Explanation:If the source contains the target content, return the location where the target first appeared in the source.
Challenge
O(n2) is acceptable. Can you implement an O(n) algorithm? (hint: KMP)
O(n2)的算法是可以接受的。如果你能用O(n)的算法做出来那更加好。(提示:KMP)
题解
对于字符串查找问题,可使用双重 for 循环解决,效率更高的则为 KMP 算法。双重 for 循环的使用较有讲究,因为这里需要考虑目标字符串比源字符串短的可能。对目标字符串的循环肯定是必要的,所以可以优化的地方就在于如何访问源字符串了。简单直观的解法是利用源字符串的长度作为 for 循环的截止索引,这种方法需要处理源字符串中剩余长度不足以匹配目标字符串的情况,而更为高效的方案则为仅遍历源字符串中有可能和目标字符串匹配的部分索引。
实现
Python:
1 class Solution: 2 def strStr(self, source, target): 3 if source is None or target is None: 4 return -1 5 6 for i in range(len(source) - len(target) + 1): 7 for j in range(len(target)): 8 if source[i + j] != target[j]: 9 break 10 else: # no break 11 return i 12 return -1
C:
1 int strStr(char* haystack, char* needle) { 2 if (haystack == NULL || needle == NULL) return -1; 3 4 const int len_h = strlen(haystack); 5 const int len_n = strlen(needle); 6 for (int i = 0; i < len_h - len_n + 1; i++) { 7 int j = 0; 8 for (; j < len_n; j++) { 9 if (haystack[i+j] != needle[j]) { 10 break; 11 } 12 } 13 if (j == len_n) return i; 14 } 15 16 return -1; 17 }
C++:
1 class Solution { 2 public: 3 int strStr(string haystack, string needle) { 4 if (haystack.empty() && needle.empty()) return 0; 5 if (haystack.empty()) return -1; 6 if (needle.empty()) return 0; 7 // in case of overflow for negative 8 if (haystack.size() < needle.size()) return -1; 9 10 for (int i = 0; i < haystack.size() - needle.size() + 1; i++) { 11 string::size_type j = 0; 12 for (; j < needle.size(); j++) { 13 if (haystack[i + j] != needle[j]) break; 14 } 15 if (j == needle.size()) return i; 16 } 17 return -1; 18 } 19 }
Java:
1 public class Solution { 2 public int strStr(String haystack, String needle) { 3 if (haystack == null && needle == null) return 0; 4 if (haystack == null) return -1; 5 if (needle == null) return 0; 6 7 for (int i = 0; i < haystack.length() - needle.length() + 1; i++) { 8 int j = 0; 9 for (; j < needle.length(); j++) { 10 if (haystack.charAt(i+j) != needle.charAt(j)) break; 11 } 12 if (j == needle.length()) return i; 13 } 14 15 return -1; 16 } 17 }
源码分析
-
边界检查:
haystack(source)
和needle(target)
有可能是空串。 - 边界检查之下标溢出:注意变量
i
的循环判断条件,如果用的是i < source.length()
则在后面的source.charAt(i + j)
时有可能溢出。 - 代码风格:
- 运算符
==
两边应加空格 - 变量名不要起
s1、s2
这类,要有意义,如target、source
- Java 代码的大括号一般在同一行右边,C++ 代码的大括号一般另起一行
- int i, j;`声明前有一行空格,是好的代码风格
- 运算符
- 是否在for的条件中声明
i
,j
,这个视情况而定,如果需要在循环外再使用时,则须在外部初始化,否则没有这个必要。
需要注意的是有些题目要求并不是返回索引,而是返回字符串,此时还需要调用相应语言的substring
方法。Python3 中用range
替换了xrange
,Python2 中使用xrange
效率略高一些。 另外需要注意的是 Python 代码中的else
接的是for
而不是if
, 其含义为no break
, 属于比较 Pythonic 的用法。
复杂度分析:
双重 for 循环,时间复杂度最坏情况下为O((n−m)∗m)。