Leetcode(28)——实现 strStr()
题目
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从
0
0
0 开始)。如果不存在,则返回
−
1
-1
−1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回
0
0
0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
提示:
- 1 1 1 <= haystack.length, needle.length <= 1 0 4 10^4 104
- h a y s t a c k haystack haystack 和 n e e d l e needle needle 仅由小写英文字符组成
题解
方法一:KMP算法——优化的nextval数组
思路
本题是经典的字符串单模匹配的模型,因此可以使用字符串匹配算法解决,常见的字符串匹配算法包括暴力匹配、 Knuth-Morris-Pratt \text{Knuth-Morris-Pratt} Knuth-Morris-Pratt 算法、 Boyer-Moore \text{Boyer-Moore} Boyer-Moore 算法、 Sunday \text{Sunday} Sunday 算法等, 这里我们使用了 Knuth-Morris-Pratt \text{Knuth-Morris-Pratt} Knuth-Morris-Pratt 算法。
关于 KMP 算法最特别的就是 next 数组和优化后的 nextval 数组了,这里不会讲解 KMP 算法,需要的可以自己去网上查找。
代码实现
class Solution {
void get_nextval(int* nextval, const string& needle){
int i=0, j=-1; // i表示后缀的单个字符下标,j表示前缀的单个字符下标
nextval[0] = -1;
// 求nextval数组
while(i < needle.length()-1){ // 修改的是 nextval[i] 的值
if(j == -1 || needle[i] == needle[j]){
++i;
++j;
if(needle[i] != needle[j]) nextval[i]=j;
else nextval[i] = nextval[j];
nextval[i] = j;
}else j = nextval[j];
}
}
public:
int strStr(string haystack, string needle) {
if(needle.length() == 0) return 0;
// 采用KMP匹配算法
int nextval[needle.length()];
get_nextval(nextval, needle);
int l1=haystack.length(), l2=needle.length();
int i = 0, j = 0; // i为主串 haystack 当前字符的下标,j为子串 needle 当前字符的下标
while(i < l1 && j < l2){
if(j==-1 || haystack[i] == needle[j]){
++i;
++j;
}else j = nextval[j];
// 不能直接比较,size_type为无符号类型,int为有符号类型,
// 如果int为负数时,假设为-1,他会先转换成无符号整形再与size_type变量进行比较,此时可能会出现-1>正数的情况
if(l2 == j)
return i-j;
}
return -1;
}
};
复杂度分析
时间复杂度:
O
(
n
+
m
)
O(n+m)
O(n+m),其中
n
n
n 是字符串
haystack
\textit{haystack}
haystack 的长度,
m
m
m 是字符串
needle
\textit{needle}
needle 的长度。我们至多需要遍历两字符串一次。
空间复杂度:
O
(
m
)
O(m)
O(m),
m
m
m 是字符串
needle
\textit{needle}
needle 的长度。我们只需要保存字符串
needle
\textit{needle}
needle 的前缀。
方法二:暴力匹配
思路
我们可以让字符串 needle \textit{needle} needle 与字符串 haystack \textit{haystack} haystack 的所有长度为 m m m 的子串均匹配一次。
为了减少不必要的匹配,我们每次匹配失败即立刻停止当前子串的匹配,对下一个子串继续匹配。如果当前子串匹配成功,我们返回当前子串的开始位置即可。如果所有子串都匹配失败,则返回 − 1 -1 −1。
代码实现
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
for (int i = 0; i + m <= n; i++) {
bool flag = true;
for (int j = 0; j < m; j++) {
if (haystack[i + j] != needle[j]) {
flag = false;
break;
}
}
if (flag) {
return i;
}
}
return -1;
}
};
复杂度分析
时间复杂度:
O
(
n
×
m
)
O(n \times m)
O(n×m),其中
n
n
n 是字符串
haystack
\textit{haystack}
haystack 的长度,
m
m
m 是字符串
needle
\textit{needle}
needle 的长度。最坏情况下我们需要将字符串
needle
\textit{needle}
needle 与字符串
haystack
\textit{haystack}
haystack 的所有长度为
m
m
m 的子串均匹配一次。
空间复杂度:
O
(
1
)
O(1)
O(1)