一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
每日刷题 2021.04.12
- leetcode原题链接:leetcode-cn.com/problems/nu…
- 难度:简单
- 方法:kmp
题目
- 实现
strStr()函数。 - 给你两个字符串
haystack和needle,请你在haystack字符串中找出needle字符串出现的第一个位置(下标从0开始)。如果不存在,则返回-1。 - 说明:
- 当
needle是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。 - 对于本题而言,当
needle是空字符串时我们应当返回0。这与C语言的strstr()以及Java的indexOf()定义相符。
示例
- 示例1
输入: haystack = "hello", needle = "ll"
输出: 2
- 示例2
输入: haystack = "aaaaa", needle = "bba"
输出: -1
- 示例3
输入: haystack = "", needle = ""
输出: 0
提示
1 <= haystack.length, needle.length <= 10^4haystack和needle仅由小写英文字符组成
解题思路
- 常规的做法:双层
for循环,10 ^ 8次方,实际运行没有超时,可以正常通过。但是如果题目中的数据给到10 ^ 5 ~ 10 ^ 6的话,就需要使用KMP字符串匹配算法来解决问题。 - 进阶做法
kmp,因为kmp的核心思想不太好理解,因此选择简单题作为入门。
KMP算法
KMP本质是:优化常规做法中,每次都从头比较匹配串的操作。如何快速在原串中找到匹配字符串的下标。kmp因为在非完全匹配的过程中(即:失败的匹配过程中),提取有效信息进行复用,以此来减少重复的匹配操作,优化时间复杂度。kmp核心思想:不相等时,回退。- 主要分为
2部分:- 匹配串的
next数组 - 原串和匹配串的匹配过程
- 匹配串的
- 第一部分:
next数组- 匹配串和自身进行匹配,所形成的数组。
- 首先我们要明白两个概念:前缀:一个字符串中不包含最后一个元素的连续的子串;后缀:一个字符串中不包含最开始一个元素的连续的子串
- 举例:字符串:
bkdjsbk,其前缀[b,bk,bkd,bkdj,bkdjs,bkdjsb];后缀:[k,bk,sbk,jsbk,djsbk,kdjsbk]。比较前缀集合和后缀集合,从中找到相等的最长的子串bk
- 举例:字符串:
- 现在我们知道了最长相等前缀是什么含义后。
- 第二部分:原串和匹配串的匹配过程
- 方法与求解
next数组的方法相似
- 方法与求解
拓展
kmp的next数组中的值,还可以统一右移,第一位赋值为-1;还可以全部减1- 以上这些处理方式,全部是在实战中更方便的处理方式。
AC代码
var strStr = function(haystack, needle) {
// 空字符串,返回0,因为空字符串是最小的子串
if(needle == '') return 0;
if(needle.length > haystack.length) return -1;
// 不存在的话,返回-1
// 使用kmp,核心就是next数组,也就是不符合就回退
function getNext(needle) {
// 模式串自己和自己进行比较
let len = needle.length;
let next = new Array(len).fill(0);
for(let i = 1,j = 0; i < len; i++) {
while(needle[i] != needle[j] && j > 0){
// 如果不想等,就需要回退
j = next[j - 1];
}
if(needle[i] == needle[j]){
// 如果相等,那么当前的最长前缀+1
j++;
}
next[i] = j;
}
return next;
}
next = getNext(needle)
// 存在的话,返回第一次出现的下标
// 根据next数组,填充对应的比较
let lenH = haystack.length,index = -1,flag = true,len = needle.length;
for(let i = 0,j = 0; i < lenH; i++) {
while (haystack[i] !== needle[j] && j > 0){
j = next[j - 1];
}
if(haystack[i] == needle[j]){
j++;
}
if(j == len) {
index = i - len + 1;
break;
}
}
// 最后都存在不相等的,需要找下一个开头
return index;
};
总结
KMP算法的时间复杂度o(m + n),其中m表示原串的长度,n表示匹配串的长度。- 常规的双层
for循环,尽管可以只遍历到m - n长度,但是时间复杂度仍然是o(m * n)