LC 28. 实现 strStr()
题目链接:LC 28. 实现 strStr()
思路:KMP算法主要是理解KMP算法的思想,核心是前缀表(next数组)的构造。
通过暴力算法找字符串中是否含有指定字串复杂度过高,可以通过观察指定字串有什么规律,来减少复杂度(剪枝)。
那么如何构造前缀表呢?(字串的字串前后缀)
前缀表的长度等于模式串的长度,前缀表第i位等于从字串开始到i的字串的相同前后缀。
也有对next数组全部减一的操作,这种情况下,是回退到与之前匹配的位置。代码细节有不同。
class Solution {
public:
//得到next数组
void getNext(int* next, const string& s){
int l = 0;//l既是下标,又是最长相同前后缀的长度
next[l] = l;//数组第0位初始化为0
for(int r=1; r<s.size(); ++r){//用r遍历字符串s,从1开始,第0位已经确定
while(l!=0 && s[l]!=s[r]){//当第l位与第r位不相同时,进行回退
//回退只改变j的值,直到找到相同的前后缀,才停止回退
//l等于0的时候跳出是为了防止数组下标越界
l = next[l-1];
}
if(s[l] == s[r])++l;//这一步的意义为,当l为0时,若第l位的char与第r位的char相同,则l为1(next[r]为1)
next[r] = l;
}
}
//返回的是下标
//先创建并构造前缀表
int strStr(string haystack, string needle) {
int next[needle.size()];//创建前缀表
getNext(next, needle);//构造前缀表
int j = 0;//回退到哪个char的下标
for(int i=0; i<haystack.size(); i++){
//若发生不匹配,则进行回退,直到匹配
while(j!=0 && needle[j]!=haystack[i]){
j = next[j-1];
}
//当跳出循环时,要不j=0;要不needle[j]==haystack[i]
//当相等的时候,j向后走一位
if(needle[j] == haystack[i])j++;
//当不等的时候,从needle[0]与haystack[i+1]开始比较
//当发现最后一位也匹配上时,跳出
if(j == needle.size()) return i-(needle.size()-1);
}
return -1;
}
};
LC 459.重复的子字符串
题目链接:LC 459.重复的子字符串
思路:KMP,若有最小重复字串,则其为最长相同前后缀所剩下的部分。如果字符串由重复字串构成,那么它的next数组最后一位就是最长前后缀的长度。若不是由重复字串构成,那么它的最后一位不是最大值。不过我们不用通过判断最后一位是否是最大值来判断是否可重复(这是必要不充分条件)。而是通过next数组最后一位来计算重复字串的长度,长度若能被整个字符串整除,就返回true,否则就false。
next数组升是慢慢往上升的,降可能是突然降的。