[Lc]28实现strStr()
Contents
题目
题解
这道题说是简单题结果花了我两天,BF算法的确简单,但是其他的字符串匹配算法真的太复杂了。。。
1. 正则表达式
这个在leetcode上超时,未进行测试。
class Solution {
public:
int strStr(string haystack, string needle) {
regex r(needle);
smatch res;
regex_search(haystack, res, r);
if(!res.empty()) return res.position(0);
else return -1;
}
};
2. 库函数法
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;//特殊情况,返回0
int pos = haystack.find(needle);
return pos;
}
};
3. BF算法
Brute-Force算法,简称为 BF算法
- 时间复杂度:最坏时间复杂度为 $O((N - L)L)$,最优时间复杂度为 $O(N)$。
- 空间复杂度: $O(1)$。
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;//特殊情况,返回0
int n = haystack.size();
int m = needle.size();//计算两个的长度
for(int i=0; i<=n-m; ++i){//两个for循环遍历两个string,注意范围到[0,n-m],字符数量不足n-m就可以停止遍历
int j = 0;//j定义在第二个for循环外面,因为下面要判断j是否遍历到neddle末尾
for(; j<m; ++j){
if(haystack[i+j] != needle[j]) break;//遍历neddle,遇到不相等的字符就跳出循环
}
if(j == m) return i;//如果遍历到neddle末尾,就返回当前i,也就是匹配到字符串的首位
}
return -1;//若全部遍历完还没有匹配,说明无匹配字符串,返回-1
}
};
4. KMP法
其效率不是很高,用的不多
该算法的详细解释可以参考阮一峰前辈的博客
- 时间复杂度$O(M+N)$
- 空间复杂度$O(N)$
class Solution {
public:
vector<int> getNext(string str){
int len = str.size();
//定义next存储部分匹配表,每个位置存该处前缀和后缀重合的最长子串
//注意第一个位置是-1,每一个位置的数字代表若该处不匹配子串下标跳到哪一位,因此每一位存的数是前一位数字的最长前缀和后缀匹配长度,最后一位不用存,因为最后一位匹配完成说明子串匹配完成。
vector<int> next;
next.push_back(-1);//先插入-1
int j=0, k = -1;//j是后缀,k是前缀
while(j<len){
//若k=-1说明又重新开时比较下一轮的子串,k跳回-1,因此j要增加一位,k跳到0
if(k == -1 || str[j] == str[k]){
j++;
k++;
next.push_back(k);
//这时k存储的是记录的重合子串最长的长度
//每次j和k增加都需要在next中记录该处的重合最长前后缀长度(这个需要画图理解一下)
} else{
k = next[k];//若出现不匹配的,k跳跃到此时next记录的该处前一位记录的的最长前缀那里
}
}
return next;
}
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;
int len1 = haystack.size();
int len2 = needle.size();
int j=0;//源串下标
int k=0;//子串下标
vector<int> next;
next = getNext(needle);
//下面这一块和getNext()函数很像,可以一起记,我会标注不同
//其实这一块最好理解,其上下有想通的地方
while(j<len1 && k<len2){//区别1,两个判断循环的条件
if(k == -1 || haystack[j] == needle[k]){//区别2,对比两个字符串
j++;
k++;
//区别3,不用在next中记录数了
} else{
k = next[k];//这个k的跳跃思想和上面类似
}
}
if(k == len2) return j-k;//若遍历完是neddle遍历完成,则说明匹配成功,返回j-k,即匹配完的源串部分长度减去子串的长度
return -1;//否则返回-1
}
};
以下两个算法比较高效,理解起来更难一点,先占坑,回头来补
5. BM法
该算法的详细解释可以参考阮一峰前辈的博客
占坑
6. Horspool算法
BM算法的简化,详解见http://www.ifcoding.com/archives/247.html
占坑
7. SUNDAY法
该算法的理解见https://wenzhiquan.github.io/2016/05/28/2016-05-28-string-match-sunday/和http://www.ifcoding.com/archives/247.html
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;
int len1 = haystack.size();
int len2 = needle.size();
int i=0, j=0;//源串和子串的下标
int m = len2;//m用于存储当前匹配过程中needle相对于haystack的下一个位置
int k = 0;//若不匹配时,k用于存此时与haystack[m]相同的最右的字符在needle中的位置
//m和k很重要,用于跳过大部分不需要比较的字符
while(i < len1){
//出现不匹配的处理
if(haystack[i] != needle[j]) {
k = len2 - 1;
while (k>0 && haystack[m] != needle[k]) {
k--;
}//找到k
i = m - k;//i移动到m-k处,即needle[k]在haystack里的位置
j = 0;//j回到needle首位
m = i + len2;//m变为现在needle的末尾在heaystack相对位置的后一位
if (m > len1) return -1;//若此时已经超出haystack,返回-1
}else{
if(j == len2-1) return i-j;//若匹配到最后一位,返回匹配的第一位的位置
i++;
j++;
}
}
return -1;
}
};
Author ChrisHRZ
LastMod 2020-04-24