Leetcode Algorithm #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
解题
一般看到字符串匹配第一个想到的就是最经典的KMP算法,但是距离上一次使用KMP算法已经过去了很久,在不查的前提下自己很难再推导出来,所以在这里重新温习一下。参考Knuth–Morris–Pratt(KMP) Pattern Matching(Substring search)
首先是对搜索目标进行一个生成prefix
预处理,比如搜索字符串p
,其索引为i
的字符,对应的prefix
值应为0~i-1
的子串中,同时为前缀和后缀的最长字符串(称这样的字符串为前后缀字符串)的结束位置。重温这个定义之后,就很好的理解了在KMP中循环向前跳的while
目的,就是不断地查看是否有这样的前后缀字符串,满足前缀后方的字符与当前想要匹配的字符相同,如果没有相同的,则标志位会回到最前端。
假设一个字符串,现在0-5和10-15的字符串构成一个前后缀字符串,现在测试位置为16的字符X,第6位字符与X不等,所以我们进行前溯,找一个更小的前后缀字符串,比如0-3和12-15为一个前后缀字符串,此时第4位的字符与X相同,所以pos[16] = 4
。
class Solution {
public:
int strStr(string haystack, string needle) {
init(needle);
int cur1 = 0, cur2 = -1, len = haystack.length(), need = needle.length();
if (need == 0)
return 0;
for (; cur1 < len; ++cur1) {
while (cur2 > -1 && needle[cur2+1] != haystack[cur1])
cur2 = pos[cur2];
if (needle[cur2+1] == haystack[cur1])
++cur2;
if (cur2 == need - 1)
return cur1 - need + 1;
}
return -1;
}
private:
int pos[50000];
void init(string needle) {
// cur1为用于设置pos值的前方指针,cur2为用于测试最长前后缀字符串的指针
int len = needle.length(), cur1 = 1, cur2 = -1;
pos[0] = -1;
for (; cur1 < len; ++cur1) {
// 不满足前缀后字符与测试字符相同和未回到原位时,前溯测试更短的前后缀字符串
while (cur2 > -1 && needle[cur2+1] != needle[cur1])
cur2 = pos[cur2];
if (needle[cur2+1] == needle[cur1])
++cur2;
pos[cur1] = cur2;
}
}
};