【力扣刷题】28. 找出字符串中第一个匹配项的下标-KMP

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

 

示例 1:

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

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

 

提示:

  • 1 <= haystack.length, needle.length <= 104
  • haystack  needle 仅由小写英文字符组成

朴素解法,不考虑剪枝的话复杂度是  的,而 KMP 算法的复杂度为 

KMP 之所以能够在  复杂度内完成查找,是因为其能在「非完全匹配」的过程中提取到有效信息进行复用,以减少「重复匹配」的消耗。

 

KMP 为什么相比于朴素解法更快:

  • 因为 KMP 利用已匹配部分中相同的「前缀」和「后缀」来加速下一次的匹配。
  • 因为 KMP 的原串指针不会进行回溯(没有朴素匹配中回到下一个「发起点」的过程)。

 

前缀表与模式串下标:前缀表具有告诉我们当前位置匹配失败,跳到之前已经匹配过的地方的能力。(字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。)

KMP精讲2

next数组:就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。

KMP精讲4

构建next数组:

KMP精讲3

class Solution {
public:
    void getNext(int* next, const string& p){
        int j=-1;
        next[0]=j;
        for(int i=1;i<p.size();i++){
            while(j>=0&&p[j+1]!=p[i])j=next[j];
            if(p[j+1]==p[i])j++;
            next[i]=j;
        }
    }
    int strStr(string s, string p) {
        int n=s.size(),m=p.size();
        if(m==0)return 0; //p=""
        vector<int>next(m);
        // getNext(&next[0],p);
        int j=-1;
        next[0]=j;
        for(int i=1;i<m;i++){
            while(j>=0&&p[j+1]!=p[i])j=next[j];
            if(p[j+1]==p[i])j++;
            next[i]=j;
        }
        j=-1;
        for(int i=0;i<n;i++){
            while(j>=0&&p[j+1]!=s[i])j=next[j];
            if(p[j+1]==s[i])j++;
            if(j==m-1)return i-m+1;
        }
        return -1;
    }
};

 

版权声明:
作者:Zhang, Hongxing
链接:http://zhx.info/archives/443
来源:张鸿兴的学习历程
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录