std::find_if
1-2 std::any_of / all_of / none_of
std::regex
std::unordered_set
/ std::unordered_map
概念 | 生活类比 | 代码里典型场景 |
---|---|---|
查找 (find) | 通讯录里翻号码——只看“是不是同一个名字” | set.find("Alice") |
匹配 (match) | 找“姓王、手机号以 188 开头”的人 | find_if 、正则、Trie … |
一句话记忆:
- 查找 = 精确相等。
- 匹配 = 满足条件,条件可以很花哨。
)std::find_if
—— 找到第一个符合条件的元素#include // find_if 所在头
#include
#include
#include
struct Person {
std::string name; // 姓名
int age; // 年龄
};
int main() {
// ① 准备数据
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
// ② 调用 find_if:
// • people.begin() / people.end() -> 搜索范围
// • Lambda 表达式 -> 匹配条件
auto it = std::find_if(people.begin(), people.end(),
[](const Person& p) {
return p.age > 30; // ③ 条件:年龄 > 30
});
// ④ 判断有没有找到
if (it != people.end()) {
std::cout << "找到的人叫 " << it->name << '\n';
} else {
std::cout << "没人符合条件\n";
}
}
时间复杂度:看元素个数,最坏 O(n)
。
何时用:要找“符合某个复杂条件”的第一条记录。
std::any_of / all_of / none_of
—— 一句话问全局#include
#include
#include
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
// 任意元素 > 3?
bool anyGT3 = std::any_of(nums.begin(), nums.end(),
[](int x){ return x > 3; });
// 所有元素都是奇数?
bool allOdd = std::all_of(nums.begin(), nums.end(),
[](int x){ return x % 2 == 1; });
// 没有元素为 0?
bool noneZero = std::none_of(nums.begin(), nums.end(),
[](int x){ return x == 0; });
std::cout << std::boolalpha // 打开布尔的 true/false 输出
<< "anyGT3 = " << anyGT3 << '\n'
<< "allOdd = " << allOdd << '\n'
<< "noneZero= " << noneZero<< '\n';
}
场景:
std::regex
正则 = 用一段特殊字符串描述“字符串长什么样”。工具刀非常锋利,新手建议先小用再深入。
#include // regex、smatch 等
#include
#include
int main() {
std::string text = "邮箱是 [email protected],记一下";
// ① 邮箱正则:原生字符串 R"(…)" 省去反斜杠转义
std::regex mailRe(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
std::smatch match; // ② 保存结果
bool found = std::regex_search(text, match, // ③ 搜索
mailRe); // 按 mailRe 匹配
if (found) {
std::cout << "找到了邮箱:" << match.str() << '\n'; // match.str() = 子串
} else {
std::cout << "没找到邮箱\n";
}
}
优点:一句话搞定复杂格式。
缺点:语法难记,超长正则性能差。
std::unordered_set / map
#include
#include
#include
int main() {
// ① 把合法邮箱放进哈希表,底层是数组 + 哈希函数
std::unordered_set<std::string> whitelist = {
"[email protected]",
"[email protected]"
};
std::string email = "[email protected]";
// ② 查找:O(1) 平均复杂度
bool ok = whitelist.find(email) != whitelist.end();
std::cout << (ok ? "合法邮箱\n" : "非法邮箱\n");
}
适用:关键词量大、查询频繁、要秒级响应。
自定义类型要自己提供
==
和std::hash<>
,否则编译不过。
#include
#include
#include
struct Log {
int level; // 日志级别
std::string msg;
};
// 匹配条件:级别是偶数且包含 "ERROR"
bool match(const Log& log) {
return log.level % 2 == 0 &&
log.msg.find("ERROR") != std::string::npos;
}
int main() {
std::vector<Log> logs = {
{1, "INFO start"},
{2, "ERROR failed opening file"},
{3, "DEBUG ..."}
};
for (const auto& l : logs) { // 范围 for:从头到尾
if (match(l)) {
std::cout << "命中日志:「" << l.msg << "」\n";
}
}
}
经验:
适合“自动补全”“敏感词过滤”“输入一篇文章一次找很多词”。
#include
#include
#include
#include
struct Node { // 节点
bool end = false; // 是否单词结束
std::unordered_map<char, Node*> next;
};
class Trie {
std::unique_ptr<Node> root = std::make_unique<Node>();
public:
void insert(const std::string& word) {
Node* cur = root.get();
for (char c : word) {
if (!cur->next[c]) cur->next[c] = new Node; // 没有就建
cur = cur->next[c]; // 指针下移
}
cur->end = true;
}
bool startsWith(const std::string& pre) const {
const Node* cur = root.get();
for (char c : pre) {
auto it = cur->next.find(c);
if (it == cur->next.end()) return false; // 断路
cur = it->second;
}
return true; // 所有字符都走通
}
};
int main() {
Trie t;
t.insert("hello");
t.insert("helium");
std::cout << std::boolalpha
<< "he 前缀存在? " << t.startsWith("he") << '\n'; // true
}
O(文本长度)
。boost::algorithm::aho_corasick
Header-only,一行 #include
即用。O(log n)
。gril → girl
)。技术 | 复杂度* | 你什么时候用 | 标准库? |
---|---|---|---|
find_if |
O(n) |
单条件、找第一条 | ✔ |
any_of 等 |
O(n) |
判断整体真/假 | ✔ |
regex |
与模式相关 | 检查 & 抽取字符串格式 | ✔ |
unordered_set/map |
O(1) 平均 |
精确值存在性、键查值 | ✔ |
Trie | O(词长) |
前缀匹配、补全 | ✘ |
Radix / TST | O(词长) |
前缀匹配且想省内存 | ✘ |
AC 自动机 | O(文本长) |
批量关键词一次过滤 | ✘ |
Suffix Array | O(子串长 log n) |
任意子串检索 | ✘ |
BK-Tree | O(log n) |
模糊(编辑距离)搜索 | ✘ |
* 粗略估计;常数、数据量对真实速度影响很大。
三行口诀
精确 → 哈希 复杂条件 → 算法 / 正则 批量前缀 / 多关键词 → Trie / AC 大文本子串 → 后缀数组 模糊相似 → BK-Tree
易踩坑
std::regex
在 GCC 4.x 很慢,老服务器慎用。new
,百万关键词会爆内存——用节点池或双数组压缩。匹配手段很多,但80% 的场景只要标准库就够。当数据规模、实时性、功能复杂度逐渐上升,再把 Trie、AC、Suffix Array 等“专业武器”搬进来。
选最简单的方案先跑起来 → 基准测试 → 再升级。