题目叙述
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
分析
使用滑动窗口进行暴力求解。窗口的大小最大是匹配字符串needle的长度。时间复杂度为 O ( m ∗ n ) O(m*n) O(m∗n)。
class Solution {
public:
int strStr(string haystack, string needle) {
//本题要注意特别考虑needle为零的情况
if(needle.size()==0) return 0;
for(int i = 0;i<haystack.size();i++){
int p = i;
int q = 0;
for(;q<needle.size();q++){
if(haystack[p]!=needle[q]) break;
p++;
}
if(q==needle.size()) return i;
}
return -1;
}
};
上面的代码中p表示的是窗口的左边界,q表示的是窗口的右边界。
下面有更简洁的写法:
class Solution {
public:
int strStr(string haystack, string needle) {
//本题要注意特别考虑needle为零的情况
if(needle.size()==0) return 0;
for(int i = 0;i<haystack.size();i++){
int q = 0;
for(;q<needle.size();q++){
if(haystack[q+i]!=needle[q]) break;
}
if(q==needle.size()) return i;
}
return -1;
}
};
Rabin Karp - 常数复杂度
有一种最坏时间复杂度也为 O(N)O(N) 的算法。思路是这样的,先生成窗口内子串的哈希码,然后再跟 needle 字符串的哈希码做比较。
因此问题主要考虑在常数时间复杂度的下求出所有对比子串的哈希值。
第一个问题:如何确定哈希规则(对一个字符串的哈希规则应当是要避免产生碰撞)哈希规则定义如下:
对于一个字符串: abcd 首先将其数字化为0123,由于题目的字符串中只包含小写的字母,那么可以将26个小写的字符转化为0-25的数字。转化为数字之后,就可以定义哈希函数为:
h
0
=
0
×
2
6
3
+
1
×
2
6
2
+
2
×
2
6
1
+
3
×
2
6
0
h_0 = 0×26 ^3 +1×26^2 +2×26^1+3×26^0
h0=0×263+1×262+2×261+3×260
即让字符串中的字母对应的数字乘以相应的权重,权重的底为26,指数为从右到左的字母序号。
写成通式为:
h
0
=
c
0
a
L
−
1
+
c
1
a
L
−
2
+
…
+
c
i
a
L
−
1
−
i
+
…
+
c
L
−
1
a
0
h_0=c_0 a^{L-1}+c_1 a^{L-2}+\ldots+c_i a^{L-1-i}+\ldots+c_{L-1} a^{0}
h0=c0aL−1+c1aL−2+…+ciaL−1−i+…+cL−1a0
其中L为needle的长度, a为26。
当窗口滑动的时候,即左边去掉一个字母,右边增加一个字母,那么我们可以依据之前的哈希结果来计算新的字符串的哈希结果,即设新的字符串的哈希结果为
h
1
h_1
h1, 那么:
h
1
=
(
h
0
a
−
c
0
a
L
)
+
c
L
h_1 = (h_0 a - c_0a^L) + cL
h1=(h0a−c0aL)+cL
此外还要避免溢出: a L a^L aL可能是一个很大的数字,因此需要设置数值上限来避免溢出。设置数值上限可以用取模的方式,即用 h % modulus 来代替原本的哈希值。
class Solution {
public:
int strStr(string haystack, string needle) {
int L = needle.size();
int n = haystack.size();
if(L>n) return -1;
int a = 26;
long modulus = (long)pow(2, 31);
long h = 0;
long ref_h = 0;
for (int i = 0; i < L; i++) {
h = (h * a + haystack[i]-'a') % modulus;
ref_h = (ref_h * a + needle[i]-'a') % modulus;
}
if (h == ref_h) return 0;
long aL = 1;
for (int i = 1; i <= L; i++) aL = (aL * a) % modulus;
for (int start = 1; start < n - L + 1; start++) {
h = (h * a - (haystack[start-1]-'a') * aL + haystack[start + L - 1]-'a') % modulus;
if (h == ref_h) return start;
}
return -1;
}
};