模拟
最直接的做法就是枚举主串中每一个可能作为起点的位置,然后判断从这里开始能否完整匹配 needle。
如果当前位置开始的每个字符都和 needle 对应位置相同,那么就返回这个起点下标;否则继续尝试下一个位置即可。
时间复杂度 $O(nm)$
空间复杂度 $O(1)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public int strStr(String haystack, String needle) { if (haystack.length() < needle.length()) { return -1; } for (int i = 0; i < haystack.length() - needle.length() + 1; ++i) { if (check(haystack, i, needle)) { return i; } } return -1; }
private boolean check(String haystack, int start, String needle) { if (haystack.length() - start + 1 < needle.length()) { return false; } for (int i = 0; i < needle.length(); ++i) { if (haystack.charAt(start + i) != needle.charAt(i)) { return false; } } return true; } }
|
KMP
朴素匹配在失配时会回退主串指针,很多比较其实是重复的。KMP 的核心就在于,失配后不回退主串,而是利用模式串自身的信息,直接把模式串滑到一个合适位置继续匹配。
具体来说,先为模式串预处理出 next 数组,表示当某个位置失配时,模式串指针下一步应跳到哪里。这样在匹配过程中,一旦字符不同,就可以快速跳过不可能匹配的部分,从而把总时间复杂度优化到 $O(n + m)$。
时间复杂度 $O(n + m)$
空间复杂度 $O(m)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| class Solution { public int strStr(String haystack, String needle) { return kmp(haystack, needle); } private int kmp(String target, String pattern) { int n = target.length(); int m = pattern.length();
if (m == 0) { return 0; } if (n < m) { return -1; } int[] next = getNext(pattern); int i = 0; int j = 0; while (i < n && j < m) { if (j == -1 || target.charAt(i) == pattern.charAt(j)) { i++; j++; } else { j = next[j]; } } if (j == m) { return i - j; } else { return -1; } } private int[] getNext(String pattern) { int n = pattern.length(); int[] next = new int[n]; next[0] = -1; int i = 0; int j = -1; while (i < n - 1) { if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) { i++; j++; next[i] = j; } else { j = next[j]; } } return next; } }
|