leetcode/src/s0028_find_the_index_of_the...

90 lines
3.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "s0028_find_the_index_of_the_first_occurrence_in_a_string.hpp"
// 构造字符串 s 的前缀表
// 在 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;
// 开始迭代
// 我们的目标是在每次迭代的过程中,找到最长相同前后缀
// 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
// 回退到什么位置呢?
// 注意观察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。
// 怎么把 j 跳到 f 的位置呢f 在 aaa 的后面aaa 是
// aaafaaa 的最长公共前缀,所以 f 的下标就是 next[j - 1]
while (j > 0 && s[i] != s[j]) {
j = next[j - 1];
}
// 接下来处理当 s[j] == s[i] 的情况
// 这种情况很简单,就是公共前后缀的长度增加了 1
// 而由于 for 语句中的 ++i 使得后缀末尾 i 已经自增了 1
// 我们只需要再让前缀末尾 j 自增 1 即可
if (s[i] == s[j]) {
++j;
}
// 两种情况都处理完了,接下来更新 next[i]
// 由于我们之前让 j 自增了 1所以其实现在的情况是
// 前缀 [0, j - 1] 和 后缀 [?, i] 相同
// 然而 next[i] 是指最长公共前后缀的长度,因此长度可以用
// j 来描述。
next[i] = j;
}
}
int S0028::strStr(string haystack, string needle) {
int haystackLen = haystack.size();
int needleLen = needle.size();
if (needleLen == 0) {
return 0;
}
// 开始创建 needle 的前缀表
int next[needleLen];
getNext(next, needle);
// j 用来索引 needle ,它有两层含义
// 1. 前缀的末尾下标
// 2. 最长相同前后缀的长度
int j = 0;
// 开始迭代
// 接下来的操作和 getNext() 中的迭代非常相似
// 不过注意,现在 i 从 0 开始迭代,之所以不像 getNext()
// 中那样从 1 开始迭代是因为 getNext() 不需要考虑 i == 0
for (int i{0}; i < haystackLen; ++i) {
// 首先讨论末尾不匹配的情况,我们需要回退 j
while (j > 0 && needle[j] != haystack[i]) {
j = next[j - 1];
}
// 接下来处理末尾相同的情况,那好说,直接往前推
if (needle[j] == haystack[i]) {
++j;
}
// 成功找到匹配字符串,返回
if (j == needleLen) {
return i - needleLen + 1;
}
}
return -1;
}