总结
- 注意边界
- 注意空串
一. 基本题目
1. BF算法(暴力匹配)
class Solution {
public:
int strStr(string haystack, string needle) {
// 0.异常处理
if (0 == needle.size()) return 0;
// 1.初始化准备
int m = haystack.size();
int n = needle.size();
// 2.暴力匹配,[0, m-n]
for (int i=0; i<=m-n; ++i) {
int j = 0;
// 开始匹配
for (; j<n; ++j) {
if (needle[j] != haystack[i+j]) break;
}
if (j == n) return i;
}
return -1;
}
};
时间复杂度O(n*m),空间复杂度O(1)
2. RK算法
2.1 没有哈希冲突的方法
这会使得哈希值比较大,可能出现溢出情况
class Solution {
public:
int strStr(string haystack, string needle) {
// 0.异常处理
if (0 == needle.size()) return 0;
// 1.准备工作
int m = haystack.size();
int n = needle.size();
// 1.1 26^0~26^(n-1)计算存储
vector<long> powers(n);
powers[0] = 1;
for (int i=1; i<n; ++i) {
powers[i] = powers[i-1] * 26;
}
// 1.2 主串哈希值计算,前后字符串有重叠部分,可以利用这个减少计算量
unordered_map<long, vector<int>> mp; //hash_value -> start_position,拉链法解决哈希冲突
// 1.2.1 首个子串哈希值计算
long long hash = 0;
for (int i=0; i<n; ++i) {
hash += (haystack[i] - 'a') * powers[n-i-1];
}
mp[hash].push_back(0);
// 1.2.2 [1, m-n)范围计算
for (int i=1; i<=m-n; ++i) {
hash = (hash - powers[n-1] * (haystack[i-1] - 'a')) * 26 + (haystack[i+n-1] - 'a');
mp[hash].push_back(i);
}
// 2.正式处理
// 2.1 计算needle哈希值
hash = 0;
for (int i=0; i<n; ++i) {
hash += (needle[i] - 'a') * powers[n-i-1];
}
// 3.返回结果
if (mp.find(hash) == mp.end()) return -1; //找不到
return mp[hash][0]; //返回第一个
}
};
由于生成哈希值的方式,是唯一的,但是太大了,该代码会产生整数溢出的问题,时间复杂度生成哈希表的时间复杂度是O(m+n),匹配子串复杂度似是O(1),空间复杂度O(m+n)
2.2 有哈希冲突的方法
降低生成哈希值的最大值,比如用字符串的每一个字符从编码相加作为哈希值,虽然会出现比较多的哈希冲突,但是可以避免整数溢出问题,出现哈希冲突就在检查一遍,也就是对哈希值相等的情况再去判断即可
class Solution {
public:
int strStr(string haystack, string needle) {
// 1.异常处理
if (needle.size() == 0) return 0;
// 2.准备工作,主要是生成主串中所有子串长度的字符串的哈希值
int m = haystack.size();
int n = needle.size();
unordered_map<int, vector<int>> mp; //拉链法解决冲突
// 2.1 首个哈希值计算
int hash = 0;
for (int i=0; i<n; ++i) {
hash += (haystack[i] - 'a');
}
mp[hash].push_back(0);
// 2.2 剩余[1, m-n]个子串哈希值计算,利用前后字符串的重复部分可以减少计算
for (int i=1; i<=m-n; ++i) {
hash = hash - (haystack[i-1] - 'a') + (haystack[n+i-1] - 'a');
mp[hash].push_back(i);
}
// 3.匹配子串,注意哈希匹配了还是要检查一遍,因为存放不同的字符串对于相同哈希值的情况
// 3.1 计算needle哈希值
hash = 0;
for (int i=0; i<n; ++i) {
hash += (needle[i] - 'a');
}
// 3.2 开始匹配
if (mp.find(hash) == mp.end()) return -1;
// 检查每个结果
for (auto &i : mp[hash]) {
int j = 0;
for (; j<n; ++j) {
if (needle[j] != haystack[i+j]) break;
}
if (j == n) return i; //匹配
}
return -1; //虽然哈希值相同,但是全都不匹配
}
};
时间复杂度O(n+m),空间复杂度O(n*最大冲突个数)