[Leetcode P28] Implement strStr()(KMP算法)

原题:

Implement strStr().

Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

刚看到这题,我就放了个string.find()上去:

return haystack.find(needle);

完美AC。
但是转念一想,这题虽然是Easy,但是为什么不练练手实现一下KMP算法呢?
算法导论里面,字符串匹配是单独的一章,讲的也比较复杂,当时看的适合还没学过计算理论,有限自动机那一块看的云里雾里,后来重新回头看才理解,但是自动机的实现是比较复杂的。
KMP算法也是看了一下思想核心,本质上就是记录前缀的模式,但是因为章节比较长,以为实现起来很麻烦。
这次重新写一遍才发现,真的很简单……

下面是正文!!——KMP

KMP算法的核心是找到一个next数组,我们知道,在字符串匹配的问题中,最大的问题就是,当我们匹配失败的时候,我们不一定要从头再来:
假设我们有字符串s和模式p,假设si,si+1,…si+k的子字符串可以和p0,p1,…pk的子字符串成功匹配,而si+k+1和pk+1无法匹配,我们应该从头再来吗?不,我们应该明白的是,si,si+1,…si+k这个子字符串里面可能包含了p的某个前缀,也就是说我们不应该从头开始匹配。
比如p=aabaacc和s=aabaaabaacc,aabaaa无法和aabaac正确匹配,但aabaaa的最后两个aa是和模式aabaacc的前两个字母重合的,我们应当从第三个字母b开始重新匹配。
我们把模式p单独提取出来,上文提到的aabaaa和aabaacc的关系,其实就是aabaa和aabaacc的关系,也就是说,我们应当统计模式的自匹配,当我们的匹配过程中断的时候,可能只需要从p的一个前缀重新开始就可以了。
next数组的长度和p等长,其中,第k个值应该等于:对p0,p1,…,pk,满足pk-m,pk-m+1,…,pk=p0,p1,…,pm的最大m,而且m不等于k。我们会发现,next[k]在很多无法重现前缀的位置都会是0。
另外,我们要知道的是,如果p0,p1,…,pk,pk+1无法匹配了,那么我们应该检查下一个可能的前缀匹配p0,p1,…,pm,pm+1是否可以成功匹配,如果pm+1和pk+1是不匹配的,没关系,index[m]里保存着更短的前缀。
next数组相当于是一个Pattern,也可以看作自动机的重新实现,而且实现起来非常简单:

// 2. kmp算法,第一次写kmp算法,被算法导论给骗了,写起来真的很简单啊,和算导上的伪代码差不多短
class Solution {
public:
    static int kmp(string haystack, string needle) {
        // 第一步,找到前缀表
        int *next = new int[needle.length()];
        next[0] = 0;
        for (int i = 1; i < needle.length(); ++i)
        {
            next[i] = needle[next[i-1]?next[i-1]+1:0] == needle[i] ? next[i-1] : 0;
        }
        // 第二步,进行匹配
        for (int i = 0; i < haystack.length(); ++i)
        {
            int index = 0;
            while(index < needle.length() && i+index < haystack.length() && haystack[i+index] == needle[index]){
                index++;
            }
            if (index == needle.length()){
                return i;
            }
            index = next[index];
            i += index;
        }
        return -1;
    }

    static int strStr(string haystack, string needle) {
        if (haystack.length() < needle.length()) return -1;
        if (needle.length() == 0) return 0;
        return kmp(haystack, needle);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值