28. 实现 strStr() :实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
理论
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
前缀和后缀都是从前往后读,只是相应的忽略第一个或最后一个
填充前缀表:前缀表的值是当前字符串最大的共同前缀后缀的长度
比如aaba,前缀有:a,aa,aab 后缀有:aba,ba,a 相同的只有a,长度为1
匹配两个字符串:
f不匹配,移动到b
下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa
aabaa在两个字符串中已经匹配,源字符串的后缀(aa)与目标字符串的前缀(aa)相同,所以目标字符串的前缀不用再匹配,直接对比目标字符串前缀之后的字符b
由图可知,当下标5的位置不匹配,所以把目标字符串的指针回退到前缀后一个位置(前缀表中的值)b
KMP算法的时间复杂度是$O(n+m)$的。
代码流程:
构建前缀表:
定义两个指针i和j,j指向前缀起始位置,i指向后缀起始位置。
next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)
j每次都从下标0开始,遍历i(遍历后缀的起始位置)
- 初始化第一个字符的最长相等的前后缀长度为0
- 遍历后缀起始位置,
- 如果nums[i]!=nums[j],前缀指针j回退到前一个元素next[]值的下标,
j=next[j-1]
类似于前缀字符串和后缀字符串的匹配,如果当前值不相等,则前缀j回退
- 如果当前nums[i]=nums[j] 则前缀指针j++
- 记录当前最长的值
匹配两个字符串:
1,定义两个指针分别指向两个字符串的首部,遍历源字符串
2,如果相等,指针前移,如果不相等,目标字符串指针需要回退,
3,如果目标字符串的指针位置等于目标字符串的长度,则匹配成功,返回起始位置源字符串的下标-长度+1
x-needle.length+1
var strStr = function(haystack, needle) {
//如果目标字符串长度为0,则返回0
if(needle.length===0){
return 0
}
//目标字符串长度不为0
//找到next[]
//初始化,next为空,第一个值为0
let next=[]
let j=0
next.push(j)
for(let i=1;i<needle.length;i++){
//如果指针i和j的值不相等,则执行回退操作
while(j>0&&needle[i]!==needle[j]){
//当前元素为needle[j]
//j回退到当前元素的前一个next[]值的下标
j=next[j-1]
}
if(needle[i]==needle[j]){
j++
}
//记录当前最长公共字串
next.push(j)
}
//匹配文本字串haystack
let p=0 //指向needle
//遍历haystack
for(let x=0;x<haystack.length;x++){
while(p>0&&haystack[x]!==needle[p]){
p=next[p-1]
}
if(haystack[x]==needle[p]){
p++
}
//匹配完整
if(p===needle.length){
return (x-needle.length+1)
}
}
return -1
};