实现strStr()
暴力解法
考虑创建两个指针i,j,i用于遍历文本串haystack,j用于循环遍历目标串needle,i和j都从0开始,当循环过程中,每个字符都能完全对上,则返回i,表示找到了字符串中第一个匹配项的下标。否则返回-1。具体代码如下。
class Solution {
public:
int strStr(string haystack, string needle) {
int maxsize = haystack.size()-needle.size();//获取最大的可能匹配项的下标
for(int i = 0; i<=maxsize; i++){//遍历i从0到最大的可能匹配项下标
int lenneedle = needle.size();//创建目标串的一个标志位。当标志位清0,表示完全匹配
int k = i;
int j = 0;
while(lenneedle>0){
if(haystack[k] == needle[j]){
k++;j++;
lenneedle--;
if(lenneedle == 0)
return i;//若标志位清0,返回当前的i,即获取的第一个匹配项的下标
}
else
break;//若不匹配跳出内循环,i++
}
}
return -1;//否则返回-1
}
};
算法的时间复杂度O(m*n),m为目标串(模式串)长度,n为文本串长度。空间复杂度为O(1)。
KMP算法
KMP算法主要用于字符串的匹配,若以暴力法比较的话,最差的情况时间复杂度为O(m*n),原因在于每次遍历都要从当前文本串比对位置的下一位进行对比,KMP算法旨在避免跳回这下一位再重新比对。KMP算法的基本思路是,根据已经比较过的数据,避免暴力解法文本串的回退。
文本串的指针一直前进,若存在与模式串不匹配的情况,模式串回退重新比较,而文本串不动,这里重要的是模式串如何回退,回退多少,这时就需要KMP算法定义的next数组,next数组的本质是目标串(模式串)中“最长的相同前后缀长度”。
求next数组步骤:
可暴力求解,或使用递归的方式来推。(周末看看能不能搞懂)
1.初始化数组,参数i,j;i指向后缀末尾位置,j指向前缀末尾位置(j还代表了包括i之前子串的最长相等前后缀的长度)。j初始化为0,i在循环中更新,i初始化为1。
2.处理前后缀不相同的情况
3.处理前后缀相同情况
4.更新next数组的值
getnext的想法还是不太会,再多看看。
class Solution {
public:
vector<int> getnext(string s){
vector<int>next(s.size(),0);
int prefix_len = 0;//当前共同前后缀的长度.
int i = 1;
next[0] = 0;
while(i < s.size()){
if(s[prefix_len] == s[i]){
next[i] = prefix_len + 1;
i++;
prefix_len++;
}
else{
if(prefix_len>0){
prefix_len = next[prefix_len - 1];
}
else{
next[i] = 0;
i++;
}
}
}
return next;
}
int strStr(string haystack, string needle) {
vector<int>next = getnext(needle);
int i = 0; //文本串的指针
int j = 0; //模式串的指针
while(i<haystack.size()){
if(haystack[i] == needle[j]){
i++;j++;
}//当两者相同时,i++,j++
else if(j >0){
j = next[j - 1];
}//否则若两者不同,且j>0时,模式串指针回退位置
else{
i++;//当j = 0时,表明都不匹配,i后移一位
}
if(j == needle.size()){//当j到达模式串尾后一位,表明已经完全匹配
return i - j;//返回i-j,即为字符串中第一个匹配项的下标。
}
}
return -1;//否则返回-1,未找到
}
};
算法的时间复杂度为O(n),空间复杂度为O(1)。
重复的子字符串
暴力解法
暴力解法考虑对所有可能子串与主串进行匹配,当匹配到符合子串则返回true,否则返回false,这里有一个需要注意的点,由于子串需要完全匹配主串,所以子串一定会出现在包含主串首字符的位置,此外,由于是子串,有两个剪枝方法,一是若为重复子串,则最少应在字符主串中出现两次,所以对子串的遍历只需要从0到主串长度/2,此外,主串长度应为子串长度的倍数,即s.size()%substring.size() == 0,若不为0,可直接跳过这段遍历。算法的时间复杂度为O(n^2),空间复杂度为O(1)。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string substring;
for(int i = 0; i < s.size()/2; i++){
substring += s[i];//构建子串,且这个子串必然包含字符串的首字符
if(s.size()%substring.size()!=0){
continue;
}
int j =0;
while(j<s.size()) {//针对文本串进行循环,若能完全循环,返回true
int k = 0;
while (substring[k] == s[j] and j < s.size()) {
k++;
j++;
if (k == substring.size() and j == s.size())
return true;
if (k == substring.size())
k = 0;
}
break;//存在不匹配的,则当前子串不能构成文本串,退出while循环
}
}
return false;//找不到能匹配的子串,返回false
}
};