题目:28. 实现 strStr()
文章链接:代码随想录
视频链接:LeetCode:实现strStr
题目链接:力扣题目链接
详解:KMP:主要应用于字符串匹配,当出现字符串不匹配是,可以知道一部分之前已经匹配的文本内容,利用该信息避免从头再做匹配
当匹配到字符串'f'不匹配时,退回到下标为2的位置开始匹配,不用重新从小标为0的位置开始匹配
前缀:不包含末尾元素的顺序组成,eg: aabaaf的前缀为a、aa、aab、aaba、aabaa
后缀:不包含首元素的顺序组成,eg: aabaaf的后缀为f、af、aaf、baaf、abaaf
前缀表:记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配,记录小标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
class Solution {
public:
void getNext(vector<int>&next, const string& s) {
// 初始化前缀表
next[0] = 0;
int j = 0; // 初始化前缀末尾
for (int i = 1; i < s.size(); i++) {
// 前缀后缀不相等的情况
while (j > 0 && s[j] != s[i]) { // 要确保j-1>=0,所以对j进行一个限制
j = next[j - 1]; //找前一位对应的回退位置
}
// 前后缀相等的情况
if (s[j] == s[i]) {
j++; // 如果相同进行加1
}
next[i] = j; // 赋值前缀表
}
}
// 实现 strStr()
int strStr(string haystack, string needle) {
// needle为需要匹配的字符串,计算该字符串的前缀表
if (needle.size() == 0) return 0;
vector<int>next(needle.size()); //初始化
getNext(next, needle); //获得前缀表
// 开始进行匹配,跟获得前缀表差不多
int j = 0;
for (int i = 0; i < haystack.size(); i++) { //不同点在于,两个字符串进行匹配时从0开始
// 不匹配的情况
while (j > 0 && needle[j] != haystack[i]) {
j = next[j - 1]; // 回退
}
if (needle[j] == haystack[i]) {
j++;
}
// 这里与getNext不用,不用构建前缀表,如果满足,直接返回
if (j == needle.size()) {
// 当j == needle.size(),i还没++
return (i - needle.size()+1);
}
}
return -1;
}
};
题目:459.重复的子字符串
文章链接:代码随想录
视频链接:LeetCode:459.重复的子字符串
题目链接:力扣题目链接
图释:
void getNext(vector<int>&next, const string& s) {
// 初始化前缀表
next[0] = 0;
int j = 0; // 初始化前缀末尾
for (int i = 1; i < s.size(); i++) {
// 前缀后缀不相等的情况
while (j > 0 && s[j] != s[i]) { // 要确保j-1>=0,所以对j进行一个限制
j = next[j - 1]; //找前一位对应的回退位置
}
// 前后缀相等的情况
if (s[j] == s[i]) {
j++; // 如果相同进行加1
}
next[i] = j; // 赋值前缀表
}
}
// 重复的子字符串
bool repeatedSubstringPattern(string s) {
// 通过计算最大前缀表来得到
vector<int> next(s.size());
getNext(next, s);
int len = s.size();
if (next[len-1]!= 0 // 如果等于的话,至少说明最后一个不能重复
&& len % (len - next[len-1]) // 最长相同前后缀的长度
== 0) {
return true;
}
return false;
}