KMP算法解析 C++代码实现

从求子串的算法题开始

实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:

输入: haystack = "hello", needle = "ll"  
输出: 2

示例 2:

输入: haystack = "aaaaa", needle = "bba"
输出: -1

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

对于这个算法题,第一反应就是把needle串一位位依次与haystack串进行比较,我们称之为朴素算法,这样的做法明显有很多的重复,例如:
KMP算法解析 C++代码实现_第1张图片
这时聪明人应该这么做:
KMP算法解析 C++代码实现_第2张图片
(图片取自LeetCode题解)

KMP算法

KMP算法是由三位大神联合发明的,不用我说你们应该也能想到KMP就分别是这三位大佬的名字首字母,那么他究竟是如何实现上述的聪明算法的呢?
这里我主要参考的大话数据结构这本书,大家如果刚开始刷算法题,也建议看完这本书补补基础再开刷,事半功倍哦!
KMP算法解析 C++代码实现_第3张图片
如图是我所说的朴素算法的过程,我们可以看到,这里面2~5步都是多余的,这是因为:假设我们已经知道匹配串不重复(这是前提,之后会说有重复怎么办),如上图的abcdex,那么第一步之后判断出前5个都对应,而第6个不对应后,不论被匹配串的第7位是什么,第②步都不可能全部对应上,因为前5个全都不对应。同理可得2到5步全都不需要,直接判断第6步即可。

那么KMP算法是如何做到这么聪明的呢,该算法使用了两个指针,分别指向模板串和匹配串。
模板串的指针不会回退,只能一步步向前加。
匹配串的指针可以回退,而怎么进行回退是通过一个next数组来做的,next数组长度与匹配串长度相等,其中每一位的值代表如果到该位字符串不相等了,那么下一步跳到匹配串的哪一位继续。

例如上图中第一步时到了匹配串的第6位不相等了,那么i应该不变,而j应该跳到匹配串的第一位,所以next数组的第六位就应该是0。下图是大话数据结构里的例子,但是要注意他的数组下标有一些问题,理解这个意思就行了。
KMP算法解析 C++代码实现_第4张图片
注意书上的j是从1开始的,但我们实际编程时数组第一位下标为0.
KMP算法解析 C++代码实现_第5张图片
理解了next数组之后我们来看一下代码实现。

vector<int> getnext(string P){
        vector<int> next(P.size());
        int q,k;//q:模版字符串下标;k:最大前后缀长度
        int m = P.size();//模版字符串长度
        next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
        for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
        {
            while(k > 0 && P[q] != P[k])//递归的求出P[0]···P[q]的最大的相同的前后缀长度k
                k = next[k-1];          //不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解  
            if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
            {
                k++;
            }
            next[q] = k;
        }
        return next;
    }

我们可以看到,如果匹配串全部相等,那么k的值会一直增加,这很好理解,这里面最难理解的就是while(k > 0 && P[q] != P[k]) k = next[k-1];这一句。这就是之前说的匹配串中如果有重复该怎么办。
以上面例子中的“abcabx”为例,前四位不等的时候next数组为“0000”,到了第4位时和开头a相等了,那么next数组接下来两位就是“12”,这是因为再比较时,前两位ab就不用再比了,因为已经相等了,如果再往后加一位

class Solution {
public:
    int strStr(string haystack, string needle) {
        if(needle.empty())
            return 0;
        vector<int> next = getnext(needle);
        int n,m;
        int i,q;
        n = haystack.size();
        m = needle.size();
        for (i = 0,q = 0; i < n; ++i)
        {
            while(q > 0 && needle[q] != haystack[i])
                q = next[q-1];
            if (needle[q] == haystack[i])
            {
                q++;
            }
            if (q == m)
            {
                return i-q+1;
            }
        }    
        return -1;
    }
};

KMP算法的优化

例如匹配串为“aaaaab”,模板串前4位为“aaac”,那么第4位检测到不等时,应该将匹配串跳到底。之后补充

你可能感兴趣的:(LeetCode)