Leetcode Algorithm #28 Implement strStr()

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;
        }
    }
};
点赞