初次接触kmp算法,确实不太好理解,后面还需要多看几次。
(1)28. 实现 strStr()
- 该题目难点在于 如何构建next 前缀表的理论解释,next数据是字符串中各个子串的一个最长相等前后缀的集合,遇到了冲突,next数组需要向前回退,这是next数组的核心所在,在计算next数组时有一种递归的感觉。
- 还是需要多分析下,回退这种方式,如果遇到前后缀不相等的,便会回退,直到 要么回退到两者有相等的在开始,要么就是起始位置。
- 还有觉得卡哥的给定最长相等前后缀的定义更为准确,公共代表是同一个东西,相等就不一定了。
- 还有的一些解法,会把next数组的值整体往右移一位,next[0] = -1, 是当遇到冲突是,就不用往前找了,直接就是冲突位置对应的next值。
class Solution {
public:
int strStr(string haystack, string needle) {
int next[needle.size()];
getNext(needle, next);
for (int i = 0; i < needle.size(); i++) {
std::cout << next[i] << std::endl;
}
int j = 0;
for (int i = 0; i < haystack.size(); i++) {
while (j > 0 && haystack[i] != needle[j]) {
j = next[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == needle.size()) {
return i - needle.size() + 1;
}
}
return -1;
}
// 先去求模板串对应的前缀表
void getNext(string s, int* next) {
// j:代表前缀末尾,同时也是前缀后缀最长相等的长度
// 注:j 的前缀末尾应该是 最长相等的前缀末尾
// i: 代笔后缀末尾;
int j, i;
//初始时
j = 0;
next[0] = 0;
for (int i = 1; i < s.size(); i++) {
while (j > 0 && s[j] != s[i]) {
j = next[j - 1];
//j = 0;
}
if (s[j] == s[i]) {
j++;
}
next[i] = j;
}
}
};
(2)459.重复的子字符串
- 对于暴力法,取子串用一层for循环即可,是因为默认的子串一定是从最前面的这个元素开始的,如果开头的元素你都不包含的话,你所搜索的子串,它能否去组成这个整个字符串其实都没有意义。
移动窗口法
- 若一个字符串能够由一个子串重复构成,那么将该字符串 两个加起来,掐头去尾,一定可以找到原始字符串。
- 原因在于任何一个由重复字符子串组成的字符串,它的前半部分和后半部分一定是相等的,注:不一定是从正中间劈开的, 例如:ababab 可以是 abab abab。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string s2 = s + s;
s2.erase(s2.begin());
s2.erase(s2.end() - 1);
if (s2.find(s) != std::string::npos) return true;
return false;
}
};
kmp法
- 如果一个字符串是由重复子串组成的,那么它的最小重复单位就是它的最长相等前后缀不包含的那个子串。可以通过数学递推证明。
- 数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。 如果不能整除的话,说明不是,例如:aabaaba,不是, 最长相等前后缀:aaba, 剩下的为 aab。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
// kmp算法
int next[s.size()];
getNext(s, next);
int delta = s.size() - next[s.size() - 1];
if (delta != s.size() && s.size() % delta == 0) {
return true;
} else {
return false;
}
}
// 先去求模板串对应的前缀表
void getNext(string s, int* next) {
// j:代表前缀末尾,同时也是前缀后缀最长相等的长度
// 注:j 的前缀末尾应该是 最长相等的前缀末尾
// i: 代笔后缀末尾;
int j, i;
//初始时
j = 0;
next[0] = 0;
for (int i = 1; i < s.size(); i++) {
while (j > 0 && s[j] != s[i]) {
j = next[j - 1];
//j = 0;
}
if (s[j] == s[i]) {
j++;
}
next[i] = j;
}
}
};