KMP主要针对场景:在一个串中查找是否出现过另一个串
前缀:包含首字母,不包含尾字母的所有子串
后缀:包含尾字母,不包含首字母的所有子串
在当前对文本串和模式串检索的过程中,若出现了不匹配,利用已经匹配的部分:
在不匹配之前已经完全相同,那么也就意味着 文本串和模式串中的这部分的最长相等前后缀相同,即模式串的前缀和文本串的后缀完全相同,那么就利用前面的最长公共前后缀长度进行跳转,重新匹配。而不是从头匹配。
前缀表(最长相等前后缀为元素的数组):双指针法
一个指向前缀末尾,一个指向后缀末尾,前缀末尾同时也是包括后缀末尾在内往前的字符串的最长相等前后缀。
前缀末尾往复循环匹配,更新后缀指针指向的元素对应的前缀表元素。
1 找出字符串中第一个匹配项的下标
class Solution {
public:
void getNext(vector<int>& next,string& needle){
int preIndex = 0;//前缀末尾,同时也是afterIndex以及afterIndex前面的字符串的最长相等前后缀
int afterIndex = 1;//后缀末尾
for(;afterIndex < needle.size();afterIndex++){//后缀末尾不断向前遍历,前缀指针往复循环匹配
while( preIndex>0 && needle[afterIndex] != needle[preIndex]){ //匹配不成功
preIndex = next[preIndex-1];//不匹配,后缀末尾就找前一个元素对应的next,进行跳转
}
if(needle[afterIndex] == needle[preIndex]) {//匹配成功
preIndex++;//afterIndex以及afterIndex前面的字符串的最长相等前后缀
}
next[afterIndex] = preIndex;
}
}
public:
int strStr(string haystack, string needle) {
//if(needle.size() == 0) return 0;
vector<int> next(needle.size(),0);
getNext(next,needle);
int needleIndex =0;
for(int i = 0;i < haystack.size();i++){
while(needleIndex>0 && needle[needleIndex] != haystack[i] ){
needleIndex = next[needleIndex-1];
}
if(needle[needleIndex] == haystack[i]) {
needleIndex++;//相等就+1,匹配到最后一位相等,那么needleIndex+1 == needle.size()
}
if(needleIndex == needle.size()) return (i-needle.size()+1);//当needleIndex到末尾后一位说明匹配成功
}
return -1;
}
};
2 重复字符串
2.1 find
将两个原字符串拼接在一起,掐头去尾之后,如果能找到原字符串,就说明是由重复的字符串所组成
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string newS = s+s;
newS.erase(newS.begin());
newS.erase(newS.end()-1);
if(newS.find(s) != std::string::npos) return true;
return false;
}
};
string的find()函数用于找出字母在字符串中的位置。
s.find(str,pos)
str:是要找的元素
pos:字符串中的某个位置,表示从从这个位置开始的字符串中找指定元素(默认从字符串的开头进行查找)
返回值为目标字符的索引,当没有找到目标字符时返回npos
其他相关用法:
find_first_of//查找第一次出现的位置
find_last_of//查找最后一次出现的位置
rfind//从后往前查找第一次出现的位置
当正向查找与反向查找得到的位置不相同,说明查找的子串不唯一
2.2 KMP
如果一个字符串是由重复子串组成,那么他的最小组成子串就是 字符串除去最长相等前后缀的那一部分
找出最小组成子串的长度,然后用字符串相除,看是否可以整除,可以整除则说明是由重复子串组成。
另外要注意代码22行的第一个条件,必须要有其最长相等前后缀长度不等于0才可以,如果最长相等前后缀为0,那就说明不是由重复子串构成。
class Solution {
public:
void getNext(vector<int>& next, string& s){
int preIndex =0;
int afterIndex = 1;
for(;afterIndex < s.size();afterIndex++){
while(preIndex>0 && s[preIndex] != s[afterIndex] ){
preIndex = next[preIndex-1];
}
if(s[preIndex] == s[afterIndex]){
preIndex++;
}
next[afterIndex] = preIndex;
}
}
public:
bool repeatedSubstringPattern(string s) {
if(s.size()==0) return false;
vector<int> next(s.size(),0);
getNext(next,s);
int length = s.size()-next[s.size()-1];//最小重复子串长度
if(next[s.size()-1] != 0 && (s.size()%length)==0) return true;
return false;
}
};