Implement strStr

Tags: Two Pointers, String, Easy

Question

Problem Statement

Implement strStr().

Returns the index of the first occurrence of needle in haystack, or -1 if
needle is not part of haystack.

题解

对于字符串查找问题,可使用双重 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. for i in range(len(source) - len(target) + 1):
  6. for j in range(len(target)):
  7. if source[i + j] != target[j]:
  8. break
  9. else: # no break
  10. return i
  11. return -1

C

  1. int strStr(char* haystack, char* needle) {
  2. if (haystack == NULL || needle == NULL) return -1;
  3. const int len_h = strlen(haystack);
  4. const int len_n = strlen(needle);
  5. for (int i = 0; i < len_h - len_n + 1; i++) {
  6. int j = 0;
  7. for (; j < len_n; j++) {
  8. if (haystack[i+j] != needle[j]) {
  9. break;
  10. }
  11. }
  12. if (j == len_n) return i;
  13. }
  14. return -1;
  15. }

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 (haystack.size() < needle.size()) return -1;
  7. for (string::size_type i = 0; i < haystack.size() - needle.size() + 1; i++) {
  8. string::size_type j = 0;
  9. for (; j < needle.size(); j++) {
  10. if (haystack[i + j] != needle[j]) break;
  11. }
  12. if (j == needle.size()) return i;
  13. }
  14. return -1;
  15. }
  16. };

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. for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
  7. int j = 0;
  8. for (; j < needle.length(); j++) {
  9. if (haystack.charAt(i+j) != needle.charAt(j)) break;
  10. }
  11. if (j == needle.length()) return i;
  12. }
  13. return -1;
  14. }
  15. }

源码分析

  1. 边界检查:haystack(source)needle(target)有可能是空串。
  2. 边界检查之下标溢出:注意变量i的循环判断条件,如果用的是i < source.length()则在后面的source.charAt(i + j)时有可能溢出。
  3. 代码风格:
    • 运算符==两边应加空格
    • 变量名不要起s1``s2这类,要有意义,如target``source
    • Java 代码的大括号一般在同一行右边,C++ 代码的大括号一般另起一行
    • int i, j;`声明前有一行空格,是好的代码风格
  4. 是否在for的条件中声明i,j,这个视情况而定,如果需要在循环外再使用时,则须在外部初始化,否则没有这个必要。

需要注意的是有些题目要求并不是返回索引,而是返回字符串,此时还需要调用相应语言的substring方法。Python3 中用range替换了xrange,Python2 中使用xrange效率略高一些。
另外需要注意的是 Python 代码中的else接的是for 而不是if, 其含义为no break, 属于比较 Pythonic 的用法,有兴趣的可以参考 4. More Control Flow Tools 的 4.4 节和 if statement - Why does python use ‘else’ after for and while loops?

复杂度分析

双重 for 循环,时间复杂度最坏情况下为 O((n-m)*m).