2021-10-1 28. 实现 strStr()(KMP算法)

注:
KMP详细解析
https://programmercarl.com/0028.%E5%AE%9E%E7%8E%B0strStr.html#其他语言版本

题目:
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

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

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

示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
示例 3:
输入:haystack = “”, needle = “”
输出:0

提示:
0 <= haystack.length, needle.length <= 5 * 104
haystack 和 needle 仅由小写英文字符组成

题解:
KMP算法

  • 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
  • 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
  • 前缀表要求的就是相同前后缀的长度。

在这里插入图片描述
长度为前1个字符的子串a,最长相同前后缀的长度为0。
长度为前2个字符的子串aa,最长相同前后缀的长度为1。
长度为前3个字符的子串aab,最长相同前后缀的长度为0。
长度为前4个字符的子串aaba,最长相同前后缀的长度为1。
长度为前5个字符的子串aabaa,最长相同前后缀的长度为2。
长度为前6个字符的子串aabaaf,最长相同前后缀的长度为0。

那么把求得的最长相同前后缀的长度就是对应前缀表的元素,如图:
在这里插入图片描述

  • 找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。
  • 为什么要前一个字符的前缀表的数值呢,因为要找前面字符串的最长相同的前缀和后缀。所以要看前一位的前缀表的数值。
  • 前一个字符的前缀表的数值是2, 所有把下标移动到下标2的位置继续比配。
  • 最后就在文本串中找到了和模式串匹配的子串了。

KMP算法中的next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。其实这并不涉及到KMP的原理,而是具体实现,next数组即可以就是前缀表,也可以为前缀表统一减一之后的值(右移一位,初始位置为-1)

复杂度分析
时间复杂度:O(n+m),其中 n 是字符串 haystack 的长度,m 是字符串 needle 的长度。我们至多需要遍历两字符串一次。

空间复杂度:O(m),其中 m 是字符串 needle 的长度。我们只需要保存字符串 needle 的前缀函数

class Solution {
public:
    void getnext(int* next,string &s){
        int j=-1;
        next[0]=j;
        for(int i=1;i<s.size();i++){
            while(j>=0&&s[i]!=s[j+1]){
                j=next[j];
            }
            if(s[i]==s[j+1]){
                j++;
            }
            next[i]=j;
        }
    }
    int strStr(string haystack, string needle) {
        if(needle.size()==0){
            return 0;
        }
        int next[needle.size()];
        getnext(next,needle);
        int j=-1;
        for(int i=0;i<haystack.size();i++){
            while(j>=0&&haystack[i]!=needle[j+1]){
                j=next[j];
            }
            if(haystack[i]==needle[j+1]){
                j++;
            }
            if(j==(needle.size()-1)){
                return (i-needle.size()+1);
            }
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值