KMP Update
This commit is contained in:
@@ -1,37 +1,35 @@
|
||||
#include "s0028_find_the_index_of_the_first_occurrence_in_a_string.hpp"
|
||||
|
||||
// 构造字符串 s 的前缀表
|
||||
// next[i] 表示 s[0]~s[i] 这个子字符串的最长相同前后缀的长度
|
||||
// 一个字符串的前缀是指从 0 开始往后到不包含末尾字符的子字符串
|
||||
// 一个字符串的后缀是指从末尾开始往前到不包含第一个字符的子字符串
|
||||
// 比如 aaba 拥有相同的最长前后缀 a 和 a
|
||||
// 前缀表的一个例子:
|
||||
// s: a a b a a f
|
||||
// next: 0 1 0 1 2 0
|
||||
// 在 getNext() 函数中,我们假设 s.length() >= 2
|
||||
void S0028::getNext(int* next, const string& s) {
|
||||
// j 有两重含义:
|
||||
// 1. 前缀的末尾下标
|
||||
// 2. 最长相同前后缀的长度
|
||||
// i 的含义是后缀的末尾下标
|
||||
// 初始化 j 为 0
|
||||
int j{0};
|
||||
// next[0] 很显然一定是为 0 的
|
||||
next[0] = 0;
|
||||
// 开始迭代,每次迭代我们都会填充 next[i]
|
||||
// i 的含义是后缀的末尾下标
|
||||
// i 从 1 开始迭代,因为 length >= 2
|
||||
// 我们没必要考虑 i == 0 的情况
|
||||
// 开始迭代
|
||||
// 我们的目标是在每次迭代的过程中,找到最长相同前后缀
|
||||
// s[0...j] == s[?...i]
|
||||
// i 从 1 开始迭代,因为 length >= 2 ,我们没必要讨论
|
||||
// i == 0 的情况
|
||||
int len = s.size();
|
||||
for (int i{1}; i < len; ++i) {
|
||||
// 当 s[j] 和 s[i] 不想等时,即前后缀不匹配的时候
|
||||
// 前缀末尾的下标 j 需要进行回退
|
||||
// 回退到什么位置呢?
|
||||
// a a a f a a a f
|
||||
// j i
|
||||
// j i
|
||||
// 回退到什么位置呢?
|
||||
// 注意观察,s[j] 和 s[i] 虽然不想等,但是前面这一段
|
||||
// aaafaaa 有着公共前后缀 aaa ,所以我们可以试着跳到
|
||||
// 前缀 aaa 的后面那个元素的位置 f,然后比较前缀 aaaf
|
||||
// 和后缀 aaaf 是否相同。
|
||||
// a a a f a a a f
|
||||
// j i
|
||||
// j i
|
||||
// 由于前缀和后缀都有着公共的 aaa ,所以我们只需要比较
|
||||
// s[j] 和 s[i] 是否相同就行了。
|
||||
// 如果不相同,继续回退,直到 j 回退到起始位置 0。
|
||||
@@ -57,39 +55,34 @@ void S0028::getNext(int* next, const string& s) {
|
||||
}
|
||||
|
||||
int S0028::strStr(string haystack, string needle) {
|
||||
int stringLen = haystack.size();
|
||||
int patternLen = needle.size();
|
||||
if (patternLen == 0) {
|
||||
int haystackLen = haystack.size();
|
||||
int needleLen = needle.size();
|
||||
if (needleLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 开始创建 pattern 的前缀表
|
||||
int next[patternLen];
|
||||
// 开始创建 needle 的前缀表
|
||||
int next[needleLen];
|
||||
getNext(next, needle);
|
||||
// j 用来索引 pattern ,它有两层含义
|
||||
// j 用来索引 needle ,它有两层含义
|
||||
// 1. 前缀的末尾下标
|
||||
// 2. 最长相同前后缀的长度
|
||||
int j = 0;
|
||||
// 开始迭代
|
||||
// 接下来的操作和 getNext() 中的迭代非常相似
|
||||
// i 用来索引 string ,它的含义是后缀末尾下标
|
||||
// 不过我们这里有个假设,那就是每次迭代开始的时候
|
||||
// string[i-j...i] 和 pattern[0...j] 相同
|
||||
// 注意,现在 i 从 0 开始迭代,之所以不像 getNext()
|
||||
// 不过注意,现在 i 从 0 开始迭代,之所以不像 getNext()
|
||||
// 中那样从 1 开始迭代是因为 getNext() 不需要考虑 i == 0
|
||||
for (int i{0}; i < stringLen; ++i) {
|
||||
// 不想等,回退 j ,思路和 getNext 一样
|
||||
// 回退之后要么 j == 0 ,要么 string[i-j...i]
|
||||
// 和 pattern[0...j] 相同
|
||||
for (int i{0}; i < haystackLen; ++i) {
|
||||
// 首先讨论末尾不匹配的情况,我们需要回退 j
|
||||
while (j > 0 && needle[j] != haystack[i]) {
|
||||
j = next[j - 1];
|
||||
}
|
||||
// 相等,那好说,直接往前推,和 getNext() 一样
|
||||
// 接下来处理末尾相同的情况,那好说,直接往前推
|
||||
if (needle[j] == haystack[i]) {
|
||||
++j;
|
||||
}
|
||||
// 成功找到匹配字符串,返回
|
||||
if (j == patternLen) {
|
||||
return i - patternLen + 1;
|
||||
if (j == needleLen) {
|
||||
return i - needleLen + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user