BF算法核心是双重循环逐字符比对:外层遍历主串起始位置,内层比对模式串,失败即回退;时间复杂度O(n×m),空间复杂度O(1);需在内层先检查i+j越界再访问s[i+j]。

BF算法的核心逻辑就是双重循环比对
BF(Brute Force)算法不依赖预处理或跳转优化,本质是「主串每个位置都尝试对齐模式串,逐字符比对」。它不快,但最直观、最易调试,适合小规模匹配或教学理解底层匹配行为。
关键点在于:外层控制主串 s 的起始偏移,内层控制模式串 p 的当前比对位置;一旦某次内层比对失败,就立即退出内层,外层推进一位继续尝试。
- 主串长度为
n,模式串长度为m,最坏时间复杂度是O(n × m),比如主串全是'a'、模式串是"aaa...b" - 不需要额外空间,空间复杂度稳定为
O(1) - 不修改原字符串,适合只读场景
边界处理必须检查 i + j
常见错误是直接用 i 控制外层循环起点,看似合理,但当 <code>m == 0(空模式串)或 m > s.length() 时容易越界或漏判。更稳妥的方式是在内层比对中动态判断索引有效性。
例如,当 j 递增后,先检查 i + j 是否越界,再取 s[i + j] —— 否则在 std::string 下触发 std::out_of_range 异常(debug 模式下)或未定义行为(release 模式下)。
立即学习“C++免费学习笔记(深入)”;
- 正确写法:
if (i + j >= s.length() || s[i + j] != p[j]) break; - 错误写法:
if (s[i + j] != p[j]) break;(未防越界) - 若已确保
m ,可提前返回 <code>-1,避免进入循环
返回值设计要区分「未找到」和「空模式串」
按惯例,BF 函数返回首次匹配的起始下标(从 0 开始),未找到返回 -1。但若 p 为空串,不同标准有不同语义:std::string::find 返回 0,而 POSIX strstr 对空串行为未定义。实际工程中建议显式约定:
-
p.empty()→ 直接返回0(多数 STL 实现兼容此行为) - 匹配成功 → 返回
i - 全部尝试失败 → 返回
-1
别依赖 size_type 的无符号特性做 -1 判断,返回类型应为 int 或带符号整型,否则 return -1 会变成极大正数。
用 std::string_view 可避免不必要的拷贝
如果函数被高频调用(比如日志过滤、协议解析),传参用 const std::string_view& s, const std::string_view& p 能避免 std::string 构造开销,尤其当输入来自字面量或已有缓冲区时。
注意:std::string_view 不拥有数据,调用方需保证其生命周期长于函数执行期;且 .data() 和 .length() 行为与 std::string 一致,原有下标逻辑完全适用。
示例片段:
int bf_search(const std::string_view s, const std::string_view p) {
if (p.empty()) return 0;
for (int i = 0; i <= static_cast<int>(s.length()) - static_cast<int>(p.length()); ++i) {
bool matched = true;
for (size_t j = 0; j < p.length(); ++j) {
if (i + j >= s.length() || s[i + j] != p[j]) {
matched = false;
break;
}
}
if (matched) return i;
}
return -1;
}这里 static_cast<int></int> 是为了安全处理长度差,防止无符号整型溢出导致循环条件恒真。
真正难的不是写对两层循环,而是想清楚空串、越界、有符号/无符号混用这三处细节——它们不出现在教科书伪代码里,却决定代码能不能进生产环境。


















