时间:2025-04-26 19:31
人气:
作者:admin
哈希表
哈希表匹配 是通过将敏感词存储在哈希表中,然后逐个检查文本中的每个子串是否存在于哈希表中。每次从文本中取出一段子串,检查该子串是否在哈希表中存在。哈希表的查找时间复杂度为 ????(1)。
AC自动机
使用Trie树存储多个敏感词,确保每个敏感词的共享前缀不会被重复存储。
失配指针用于在匹配失败时跳转到下一个可能的匹配位置,避免重新扫描文本。
AC自动机在搜索阶段的时间复杂度为 O(m),其中 m 是文本长度。
// 构建Goto表(Trie树)
void AcString::BuildGotoTable() {
for (size_t i = 0; i < patterns.size(); ++i) {
const std::string& word = patterns[i].pattern;
int current = 0; // 从根节点开始
for (char ch : word) {
if (nodes[current].sons.find(ch) == nodes[current].sons.end()) {
AddState(current, ch);
nodes[current].sons[ch] = nodes.size() - 1;
}
current = nodes[current].sons[ch];
}
// 在最后一个节点添加输出
nodes[current].output.push_back(i);
}
}
2.Fial指针的构建
void AcString::BuildFailTable() {
std::queue<int> q;
// 初始化根节点的子节点的fail指针为根节点
for (auto& pair : nodes[0].sons) {
int child = pair.second;
nodes[child].fail = 0;
q.push(child);
}
// BFS遍历Trie树,构建fail指针
while (!q.empty()) {
int current = q.front();
q.pop();
for (auto& pair : nodes[current].sons) {
char ch = pair.first;
int child = pair.second;
// 寻找current节点的fail指针指向的节点的子节点是否有字符ch
int fail = nodes[current].fail;
while (fail != -1 && nodes[fail].sons.find(ch) == nodes[fail].sons.end()) {
fail = nodes[fail].fail;
}
if (fail == -1) {
nodes[child].fail = 0; // 回到根节点
} else {
nodes[child].fail = nodes[fail].sons[ch];
// 将fail节点的输出添加到当前节点的输出
for (int index : nodes[nodes[fail].sons[ch]].output) {
nodes[child].output.push_back(index);
//如果 fail == -1,表示当前节点没有找到合适的失败跳转路径,因此将子节点 child 的 fail 指针指向根节点 nodes[0]。
否则,将子节点 child 的 fail 指针指向 fail 节点的子节点(即 nodes[fail].sons[ch],表示可以通过 fail 节点继续匹配字符 ch)。
接着,将 fail 节点的输出模式(output)追加到子节点 child 的 output 列表中。这一步是因为 fail 节点已经匹配到了一部分模式,所以如果 child 节点也到达这个状态,它应该继承这些匹配的模式。
}
}
q.push(child);
}
}
}
上一篇:算法day04-链表篇(2)
下一篇:算法——链表总结及扩展题