算法概述
KMP算法主要用于串匹配,于2024/12/9日做leecode题时学习并记录,若需要回顾,搜索大佬博客进行查看,这里留下记录,便于之后回顾学习。
创建一个前缀表为重中之重,使用next指针完成这个任务
前缀表记录了每一位的最长相等前后缀的长度,通过此表,将模式串回溯到上一个匹配的位置再进行对比,用空间换时间。常常在使用时将前缀统一-1。
长度为前4个字符的子串aaba,最长相同前后缀的长度为1。
长度为前5个字符的子串aabaa,最长相同前后缀的长度为2。
长度为前6个字符的子串aabaaf,最长相同前后缀的长度为0。
下面看题。
给你两个字符串 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
仅由小写英文字符组成
解法
暴力解法,直接主串和模式串对比,不同就回溯模式串到第一个再进行对比。
class Solution {
public:
int strStr(string haystack, string needle) {
int m,n,num,flag;
m = haystack.length();
n = needle.length();
for(int i =0;i+n<=m;i++)
{
flag = 1;
for(int j=0;j<n;j++)
{
if(haystack[j+i] != needle[j])
{
flag = 0;
}
}
if(flag == 1)
return i;
}
return -1;
}
};
KMP算法解决
前缀表的产生
void getNext(int* next,const string &s)
{
int j = -1;//前缀表统一减一
next[0] = j;
for(int i=1;i<s.size();i++)//i从1开始,因为j初始为-1,前缀表第一个为0
{
while(j>=0 && s[i] != s[j+1])//前后缀不相同
{
j = next[j];//向前回溯
}
if(s[i] == s[j+1])//找到相同前后缀
{
j++;//向后移动
}
next[i] = j;//将j前缀长度赋给前缀表
}
}
问题解决
class Solution {
public:
void getNext(int* next,const string &s)
{
int j = -1;//前缀表统一减一
next[0] = j;
for(int i=1;i<s.size();i++)//i从1开始,因为j初始为-1,前缀表第一个为0
{
while(j>=0 && s[i] != s[j+1])//前后缀不相同
{
j = next[j];//向前回溯
}
if(s[i] == s[j+1])//找到相同前后缀
{
j++;//向后移动
}
next[i] = j;//将j前缀长度赋给前缀表
}
}
int strStr(string haystack, string needle) {
int j = -1;
int next[needle.size()];
if(needle.size() == 0)//模式串为空的情况
return 0;
getNext(next,needle);//创建前缀表
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])//匹配,ij同时后移
{
j++;
}
if(j == (needle.size()-1))//模式串匹配完,在主串里
{
return (i - needle.size() + 1);
}
}
return -1;
}
};