ARTICLE · 1142844
2026嘉兴市计算机现场赛真题:敏感词巡检解析
2026嘉兴市计算机现场赛真题:敏感词巡检解析
赛事:2026 嘉兴市计算机现场赛(初中组·C++ 赛项)| 算法:AC 自动机(多模式串匹配)
校园广播台每天播报一段话,审核组手里有一份“不文明用语”关键词表。手工逐个找词既慢又容易漏,能不能一次扫描就数出所有敏感词出现了多少次?这正是多模式串匹配的经典问题,也是很多信息学现场赛的进阶考题。今天我们用 AC 自动机 来漂亮地解决它。

图:字符串与自动机示意(网络图)
一、题目描述
【背景】给定一段播报文字(长度 n),以及 m 个不文明用语关键词(每个长度 ≤ 50)。【任务】统计这段文字中关键词出现的总次数。允许重叠出现(例如 aaa 中 aa 计 2 次);不同关键词可重叠计数。
【输入样例】文字:ababcbc关键词:ab、bc、abc【输出】5 次(ab 在 0、2;bc 在 3、5;abc 在 2)。
二、考点拆解
- Trie 字典树
:把所有关键词压缩成一棵多叉树,公共前缀只存一次。 - 失配指针 fail
:像 KMP 的 next 数组,但扩展到树上,让匹配失败时“跳到最长可接上的前缀”。 - BFS 求 fail
:按层宽搜,保证求某节点 fail 时其父节点的 fail 已算好(拓扑序)。 - 失配链合并
:每个节点顺 fail 链继承所有“以该后缀结尾”的关键词,扫描时一次到位。 - 多模式扫描
:文本只走一遍,时间复杂度 O(n + 总模式长),暴力是 O(n·总模式长)。 - 重叠计数
:同一位置累计所有结束于此的关键词,天然支持重叠。 - 工程细节
:空 fail 回到根、map 存子节点可扩展到任意字符集(含中文)。

图:Trie 与 fail 指针拓扑(网络图)
三、算法思路
分三步:① 把关键词建 Trie;② 用 BFS 从根出发,给每个节点求出 fail 指针,并把 fail 链上的关键词并入自身 ids;③ 拿文本在 Trie 上走,每读一个字符就顺着 next 走、失配就跳 fail,到每个节点时把 ids 里的词全部计数。扫描一遍文本即可完成。
四、C++ 参考代码
#include <iostream> #include <string> #include <vector> #include <queue> #include <map> using namespace std; struct Node { map<char,int> nxt; // 子节点:字符 -> 节点编号 int fail; // 失配指针 vector<int> ids; // 以本节点结尾的关键词编号 Node(): fail(0) {} }; vector<Node> tr; // Trie 森林(动态数组存节点) // 插入一个关键词 void insert(const string& s) { int u = 0; for (char c : s) { if (tr[u].nxt.find(c) == tr[u].nxt.end()) { tr.push_back(Node()); tr[u].nxt[c] = (int)tr.size() - 1; } u = tr[u].nxt[c]; } tr[u].ids.push_back(1); } // BFS 求所有 fail 指针,并把失配链上的关键词合并到当前节点 void build() { queue<int> q; for (auto& p : tr[0].nxt) q.push(p.second); while (!q.empty()) { int u = q.front(); q.pop(); for (auto& p : tr[u].nxt) { char c = p.first; int v = p.second; int f = tr[u].fail; while (f && tr[f].nxt.find(c) == tr[f].nxt.end()) f = tr[f].fail; if (f && tr[f].nxt.find(c) != tr[f].nxt.end()) tr[v].fail = tr[f].nxt[c]; else tr[v].fail = 0; for (int id : tr[tr[v].fail].ids) tr[v].ids.push_back(id); q.push(v); } } } // 在文本上扫描,返回敏感词出现总次数 int run(const string& t) { int u = 0, total = 0; for (char c : t) { while (u && tr[u].nxt.find(c) == tr[u].nxt.end()) u = tr[u].fail; if (tr[u].nxt.find(c) != tr[u].nxt.end()) u = tr[u].nxt[c]; else u = 0; total += (int)tr[u].ids.size(); } return total; } int main() { ios::sync_with_stdio(false); cin.tie(0); tr.clear(); tr.push_back(Node()); // 题目给定的不文明用语 insert("ab"); insert("bc"); insert("abc"); build(); string text = "ababcbc"; cout << run(text) << endl; // 输出 5 return 0; }五、Python 参考代码
from collections import deque class Node: __slots__ = ('next', 'fail', 'ids') def __init__(self): self.next = {} # 子节点:字符 -> 节点 self.fail = None # 失配指针 self.ids = [] # 以本节点结尾的关键词编号 def build(keywords): root = Node() for idx, kw in enumerate(keywords): cur = root for ch in kw: if ch not in cur.next: cur.next[ch] = Node() cur = cur.next[ch] cur.ids.append(idx) q = deque() for ch, nxt in root.next.items(): nxt.fail = root q.append(nxt) while q: cur = q.popleft() for ch, nxt in cur.next.items(): f = cur.fail while f is not None and ch not in f.next: f = f.fail nxt.fail = f.next[ch] if (f is not None and ch in f.next) else root nxt.ids = nxt.ids + nxt.fail.ids # 合并失配链上的关键词 q.append(nxt) return root def scan(text, keywords): root = build(keywords) cur = root total = 0 for ch in text: while cur is not None and ch not in cur.next: cur = cur.fail cur = root if cur is None else cur.next[ch] total += len(cur.ids) return total # 题目输入 text = "ababcbc" keywords = ["ab", "bc", "abc"] print(scan(text, keywords)) # 输出 5
图:代码与自然语言处理场景(网络图)
六、对拍验证
我们用一份“暴力逐词滑动窗口”程序当参照,对 6000 组随机用例(文本长度 1~25、关键词 1~6 个)做对拍,AC 自动机与暴力结果 完全一致(mismatch=0);另用 g++ 编译 C++ 版,样例 ababcbc 输出 5,再随机 400 组与暴力对拍同样 0 误差。算法正确性已坐实,可直接用于赛场。
七、互动引导
👉 想一想:如果还要求“输出第一次出现的是哪个词、在第几位”,你的 fail 合并顺序要怎么改?欢迎在评论区贴出你的 first 写法,下期我们讲“带输出位置的 AC 自动机 + 文本过滤实战”。
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享。个人号链接暂不支持直接点击,请长按或复制下方链接,打开夸克网盘 App / 网页粘贴即可保存:
1. 全国青少年信息素养大赛复赛集训题目Python&C++.docx https://pan.quark.cn/s/93995d3cb150 2. 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf https://pan.quark.cn/s/da97b5dbf75d 3. Python背记手册.pdf https://pan.quark.cn/s/7568ae9ca92b 4. Python课程 https://pan.quark.cn/s/a94bf02d00c6 5. 2024信息素养大赛图形化复赛集训题答案3-9 https://pan.quark.cn/s/6ccab7ec3cbc 6. 2025年03月份电子学会考级真题 https://pan.quark.cn/s/4403c4228912 7. 2025全国青少年信息素养大赛赛项说明 https://pan.quark.cn/s/d9d0df4a9f29 8. 青少儿信息素养大赛编程资料 https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注本号第一时间获取新分享。