【力扣刷题】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 的原串指针不会进行回溯(没有朴素匹配中回到下一个「发起点」的过程)。
前缀表与模式串下标:前缀表具有告诉我们当前位置匹配失败,跳到之前已经匹配过的地方的能力。(字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。)
next数组:就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。
构建next数组:
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
二维码
文章目录
关闭