【leetCode】28_实现strStr()

主要是回顾一下kmp算法。

kmp分两个步骤,1,找next数组;2,匹配。

kmp之所以困惑的重要原因就在于,不知道找next数组的时候实在干什么。

这里用一句话说明白:找next[i],其实是在找,使得pattern[0:k)==pattern[i - k: i)的最大的k(注意是左闭右开区间)。

done!

为什么要找这个使得 i 前的 k 个字符和pattern从0开始的 k 个字符相同的最大的k呢?这个从蠢办法谈起,比较容易理解。朴素的算法是,如果不匹配,那么pattern向后挪一位,继续从pattern[0]开始匹配。

考虑匹配到第 pattern[m],此时被匹配的串为s,匹配到了第 n 个位置,但是pattern[m] 和 s[n] 不匹配,也就是说

s[n - m: n) == pattern[0: m),s[n] != pattern[m],那么我们将pattern向后挪一位,开始匹配,如果这时候pattern可以匹配字符串,可以推出,pattern[0: m-1)==s[n - (m - 1): n)==pattern[1:m)。后边两个相等的原因请看红色的式子。

连等的第一项和第三项看到没,这个就是我们之前所说的pattern[0:k)==pattern[i - k: i),此时k=m-1。

也就是说,如果pattern向后挪一位如果匹配,那么必满足pattern[0: i - 1)==pattern[i - (i - 1): i)。

同理,如果pattern向后挪两位如果匹配,那么必满足pattern[0: i - 2) == pattern[i - (i - 2): i)。

以此类推。

所以我们要找的就是:

对pattern的第i个位置,使得pattern[0:k)==pattern[i - k: i)的最大的k(注意是左闭右开区间),这个位置意思是,如果匹配到pattern[i] 不匹配,那么如果最终匹配,那么至少,要重置到pattern[k]开始匹配。

具体找的方式,是用贪心法去找。用贪心法的原因也很简单,当pattern[0:k)==pattern[i-k: i)时,如果pattern[k] == pattern[i],则pattern[0:k + 1)==pattern[(i+1)-(k+1): i+1),也就是说,对于pattern的第i个位置,如果 i 前的 k 个字符和pattern从0开始的 k 个字符相同,对 pattern 的第 i+1 个位置,只需要检查pattern第 i+1 个位置和第 k 个位置是否相同就可以,如果相同,那是大好事(第 i+1 个位置的最大的k也找到了),如果不同,才需要特殊处理,处理的方式类似回溯。

class Solution {
public:
    int *next;
    int strStr(string haystack, string needle) {
        return kmp(haystack, needle);
    }
    int kmp(const string &s, const string &pattern) {
        this->next = new int[pattern.length() + 5];
        memset(this->next, 0, pattern.length() + 5);
        this->next[0] = -1;
        this->next[1] = 0;
        int index = 0;
        for (int i = 2; i < pattern.length(); ) {
            if (index == -1) {
                this -> next[i] = (pattern[i - 1] == pattern[0]);
                index = 0;
                i ++;
            }
            else if (pattern[i - 1] == pattern[index]) {
                this -> next[i] = ++ index;
                i ++;
            }
            else {
                index = this->next[index];
            }
        }
        int i, j;
        for (i = 0, j = 0; i < s.length() && j < pattern.length(); ){
            if (s[i] == pattern[j]){
                i ++;
                j ++;
            }
            else {
                j = next[j];
                if (j == -1) {
                    j = 0;     
                    i ++;
                }
            }
        }
        if (j == pattern.length()) {
            return i - pattern.length();
        }
        return -1;
    }
};

 

你可能感兴趣的:(leetCode)