LeetCode第28题 实现strStr()之KMP算法(C++)【代码已提交成功】

初步思路

这是一道难度为简单的题,所以不熟悉的话可能第一反应就是朴素匹配的算法。但因为考研的时候学过数据结构,所以我第一反应是应该使用KMP算法,并私以为难度不应该归为简单,毕竟KMP算法代码虽然比较简单,但理解起来还是比较难。下图为LeetCode上的提交记录。
LeetCode上的提交记录
神奇的是在这个图片中,最上面的为朴素匹配算法,最下面为KMP算法。大家觉得更高级的算法竟然不如最直接暴力的算法????
LeetCode上的评分机制参考一下就行了,大家也不必深究,应该是它上面提供的评分算例比较简单,体现不出KMP算法的优势。中间一些解答错误,便是因为我觉得朴素匹配算法非常简单,没有认真调试,中间有个容易忽略的问题,后文我会提到。

朴素匹配算法

由于朴素算法便于理解,并可作为KMP算法的引子,所以先介绍这种算法。先直接上代码。

class Solution {
public:    
    int strStr(string haystack, string needle) {
        if(needle.empty())
            return 0;
        
        int i=0;//源串
        int j=0;//子串
        int k=i;
        int len1=haystack.size();
        int len2=needle.size();
        while(i<len1&&j<len2) {
            if(haystack[i]==needle[j]) {
                i++;
                j++;
            } else {
                i=k++;//用k标识i起始位置
                j=0;
            }
        }
        if(j>=len2) {
            return i-j;
        }
        return -1;
    }
};

haystack为作为模版的源串,needle为待匹配的子串。实现逻辑就是从haystack的一个字符开始,与needle的第一个字符比较,如果相同则haystack的第二个字符与needle的第二个字符进行比较,若不同则haystack移动到haystack的第二个字符,重新开始与needle的第一个字符比较。如此循环下去,直到找到完全匹配的位置,或是haystack已经搜索完毕,但还没找到匹配的位置时结束。

但这种朴素匹配算法的时间复杂度为O(mn),因为中间可能有些比较可以跳过,但是它都得按顺序一一的比较过去(比如ABBBBBBABA中去找ABA)。而接下来介绍的KMP算法就可以减少无用的比较,直接跳到有效的位置。KMP算法的时间复杂度为O(m+n)。

需要注意的是:k用来标记当前源串i的起始位置,否则i遍历到后面会丢失其开始的位置。而j不需要被记录,因为若匹配失败,j都是从头开始。(提交失败的三次都是因为此原因被我忽略。)

KMP算法

KMP算法则是对上述算法的一个改进。它首先求出一个NEXT数组,其指明若发生匹配失败该跳到哪个位置开始重新进行匹配;然后利用NEXT数组对上述算法进行改进。上述算法需要按顺序一个一个往后匹配,而KMP利用NEXT数组直接跳到有效的位置。

NEXT数组

vector<int> getnext(string str)
        {
            int len=str.size();
            vector<int> next;
            next.push_back(-1);//next数组初值为-1
            int j=0,k=-1;
            while(j<len-1)
            {
                if(k==-1||str[j]==str[k])//str[j]后缀 str[k]前缀
                {
                    j++;
                    k++;
                    next.push_back(k);
                }
                else
                {
                    k=next[k];
                }
            }
            return next;
        }

NEXT数组的求解目的主要就是求子串中前缀码和后缀码最大的重合情况。(如ABBABABBBA。)

【KMP算法易懂版-天勤率辉2020全程班公开一期讲解】指路该视频,简单易懂地介绍了NEXT数组的手动求解方法。

在理解了手动求法后,求NEXT数组的代码就很好理解了。
NEXT数组都是从前往后进行,所以求得next[i]后next[i+1]的求值可以分两种情况讨论:

  1. 若pi等于pj,则next[i+1]=j+1;因为j为当前最长的前后缀码重合情况,若pi等于pj,则相当于在当前的情况下加1。
  2. 若pi不等于pj,只需将j赋值为next[j],再对pi和pj进行比较。

利用NEXT数组改进朴素匹配算法

得到NEXT数组后,对朴素匹配算法中按顺序依次后挪改为根据NEXT数组寻找下一个位置即可。

int strStr(string haystack, string needle) {
        if(needle.empty())
            return 0;
        
        int i=0;//源串
        int j=0;//子串
        int len1=haystack.size();
        int len2=needle.size();
        vector<int> next;
        next=getnext(needle);
        while((i<len1)&&(j<len2))
        {
            if((j==-1)||(haystack[i]==needle[j]))
            {
                i++;
                j++;
            }
            else
            {
                j=next[j];//获取下一次匹配的位置
            }
        }
        if(j==len2)
            return i-j;
        return -1;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法C++代码实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值