【算法训练 day9 KMP算法】


一、找出字符串中第一个匹配项的下标-LeetCode 28

Leecode链接: leetcode 28
文章链接: 代码随想录
视频链接: B站

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

示例1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 06 处匹配。
第一个匹配项的下标是 0 ,所以返回 0

思路

文本串为haystack,模式串为needle。kmp匹配特殊在于在遍历两个字符串时,如果当前文本串与模式串的某一个字符不匹配,kmp不会让模式串的指针从头重新遍历,而是寻找匹配好的子串后一项,从该项继续匹配,如下图所示。aa元素就是匹配好的子串,所以需要移动到aa后一项。

在这里插入图片描述
关于匹配好的子串的理解,换个名字也就是听的最多的名字:最长公共前后缀的子串。前缀子串就是不包含最后一个字符的子串,后缀子串就是不包含首个字符的子串。

以上图为例,把f去除后,needle中前缀子串有:a、aa、aab、aaba;后缀子串有:a、aa、baa、abaa。可见,最长公共前后缀字串为aa。aa也就是匹配好的子串。

因为f跟在后缀aa之后,而在前缀中也有aa子串,f不匹配,那么是不是可以将j移动到前缀的aa后一项继续匹配,从而避免将j移动到needle的开头了?

那现在问题就集中于:如何让j指针移动到needle的b元素呢,这时需要一个next数组,这个数组作用就是当j指针与i指针不匹配时,会告诉我们j指针该移动到哪个下标。

关于next的求解过程,它的求解过程与kmp匹配过程类似,就是加了个记录该跳到哪的数组next。求解时,使用两个指针j、i(这里指针与上面图中指针不是一个意思),i指向后缀末尾,j指向前缀末尾。j初始化为0,i初始化为1,为什么为1,因为只有为1时才能比较,next[0]初始化为0。首先判断如果i与j的字符相等,那就让next[i] = 1,且让j++,表示前后缀有一个相等,这很好理解;如果不等,那就让next[i] = 0,j不变。这里关于j何时自加我的的理解是:如果相等,表示前缀与后缀有一个字符已经相等了,因为j初始值为0,j需要自加为1然后将这个值赋值给next,注意,这个值就是前缀子串与后缀子串匹配成功的下一个位置,也是后面不等时需要退到下标值。如果不等,前缀的尾字符不满足后缀的尾字符,那么表明j需要回退,表示本次匹配失败需要看看前面的字符是否还有匹配的,直到回退到前缀的首字符,回退的原理与kmp匹配失败时回退的策略一致。

重复一遍:为什么要回退到next[j-1],因为当前字符不等了,我需要知道排除这个不字符后,前面的字符串中还有没有匹配好的字符(也就是最长公共前后缀子串),如果有,我就移动到那个位置。

可以画一个数组写出next数组,这样会更有体会

实现代码

//cpp
class Solution {
public:
	//求next数组函数
    void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }

	//查找匹配的字符函数
    int strStr(string haystack, string needle) {
        if (needle.size() == 0) {
            return 0;
        }
        vector<int> next(needle.size());
        getNext(&next[0], needle);
        int j = 0;
        for (int i = 0; i < haystack.size(); i++) {
            while(j > 0 && haystack[i] != needle[j]) {
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size() ) {
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};

问题

忘了算法原理,当作复习。

总结

kmp算法需要多次理解,重点在于next数组存了什么,i指什么,j指什么。


  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值