P1713 AC 代码

括号匹配检查器

题目描述

假设一个表达式有英文字母(小写)、运算符(+-*/)和左右小(圆)括号构成,以 @ 作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则输出 YES;否则输出 NO。表达式长度小于 255255,左圆括号少于 2020 个。

输入格式

一行:表达式。

输出格式

一行:YES 或 NO

输入输出样例

输入 #1复制

2*(x+y)/(1-x)@

输出 #1复制

YES

输入 #2复制

(25+x)*(a*(a+b+b)@

输出 #2复制

NO

说明/提示

表达式长度小于 255255,左圆括号少于 2020 个。

#include <iostream>
#include <stack>
using namespace std;

int main() {
    string s;
    cin >> s;
    stack<char> stk;
    bool flag = true;
    for (int i = 0; i < s.length() && flag; i++) {
        if (s[i] == '(') {
            stk.push(s[i]);
        } else if (s[i] == ')') {
            if (stk.empty()) {
                flag = false;
            } else {
                stk.pop();
            }
        }
    }
    if (!flag || !stk.empty()) {
        cout << "NO" << endl;
    } else {
        cout << "YES" << endl;
    }
    return 0;
}

<think> 首先,用户查询是“我想查找AC代码的相关信息 请问AC代码介绍”,意思是“我想查找AC代码的相关信息,请介绍AC代码”。 从系统级指令中,我知道我需要使用中文回答,确保LaTeX语法正确(行内数学表达式用$...$,独立公式用$$...$$),并且生成相关问题。回答中引用的段落末尾添加引用标识。 用户提供了三个引用: - 引用[1]:关于AC自动机的详解,包括原理、优化分析和代码实现。提到了多模式串匹配的代码片段。 - 引用[2]:解释了AC自动机是在Trie树上构建的,有失败指针,并描述了构建过程。 - 引用[3]:讨论了AC自动机原理及代码实现,包括模式匹配过程。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个上下文中,没有提到上一次的问题或回答,所以我应该基于当前查询和提供的引用来回答。 用户查询是“AC代码”,我认为“AC代码”可能指的是“AC自动机的代码”,因为在引用中都是关于AC自动机的。AC自动机是Aho-Corasick自动机,用于多模式串匹配。 所以,我应该介绍AC自动机及其代码。 结构化我的回答: 1. **介绍AC自动机**:简要解释什么是AC自动机及其用途。 2. **原理**:基于引用,解释AC自动机是如何工作的,包括Trie树、失败指针等。 3. **代码实现**:提供或描述代码片段,从引用中提取。 4. **生成相关问题**:在回答结束时,添加相关问题部分。 确保回答真实可靠,结构清晰。 关键点来自引用: - AC自动机是在Trie树上构建的,有失败指针(引用[2])。 - 构建失败指针需要树的广度遍历(引用[2])。 - 模式匹配过程:遍历文本串,从根节点开始,根据字符跳转到子节点,然后跳失败指针收集匹配(引用[1]和[3])。 - 代码示例:引用[1]提供了伪代码。 在回答中,我应该自然地引入引用标识。例如,在描述原理时,添加[^1]、[^2]或[^3]。 生成相关问题:基于AC自动机的主题,如优化、应用、实现细节等。 最终回答结构: - 开头介绍AC自动机。 - 详细解释原理。 - 描述代码实现。 - 结束相关问题。 确保使用中文。 现在,构建回答。 </think> ### AC自动机(AC自动机代码)介绍 AC自动机(Aho-Corasick自动机)是一种高效的多模式串匹配算法,用于在单个文本串中同时查找多个模式串的出现位置。它由Alfred V. Aho和Margaret J. Corasick于1975年提出,广泛应用于字符串搜索、文本分析(如敏感词过滤)、生物信息学等领域。AC自动机基于Trie树(字典树)构建,并通过添加“失败指针”(fail pointer)来优化匹配过程,确保在匹配失败时能快速回退到最长后缀节点,避免从头开始搜索,从而提升效率。下面我将从原理、构建过程和代码实现三个方面逐步介绍[^1][^2][^3]。 #### 1. **AC自动机原理** AC自动机的核心是Trie树和失败指针机制: - **Trie树结构**:每个节点代表一个字符,从根节点到叶子节点的路径构成一个模式串。例如,模式串集合{"he", "she", "his"}构建的Trie树中,根节点连接子节点'h',再连接到'e'或'i'等。 - **失败指针(fail pointer)**:每个节点都有一个失败指针,指向当当前字符匹配失败时应跳转的节点。失败指针指向当前节点对应模式串的最长后缀节点。例如,对于节点"she"中的'e',其失败指针可能指向"he"中的'e',因为"he"是"she"的后缀[^2]。 - **匹配过程**:遍历文本串时,从根节点开始: - 如果当前字符匹配子节点,则移动到子节点。 - 如果匹配失败,则沿着失败指针跳转,直到找到匹配或回到根节点。 - 在跳转过程中,收集所有经过节点的输出(即匹配的模式串),避免重复统计[^1][^3]。 数学上,匹配过程的时间复杂度为$O(n + m + k)$,其中$n$是文本串长度,$m$是所有模式串总长度,$k$是匹配次数。这比暴力匹配($O(n \times m)$)高效得多[^1]。 #### 2. **构建过程** AC自动机的构建包括两个步骤:构建Trie树和添加失败指针。 - **构建Trie树**:将所有模式串插入Trie树。例如,模式串"abc"插入后,根节点有子节点'a','a'有子节点'b',依此类推。 - **添加失败指针**(使用广度优先遍历BFS): - 根节点的失败指针为null。 - 根节点的子节点失败指针指向根节点。 - 其他节点:设当前节点为$u$,其父节点为$p$,字符为$c$。则$u$的失败指针指向$p$的失败指针节点(记作$q$)的子节点中字符$c$对应的节点;如果$q$没有字符$c$的子节点,则递归检查$q$的失败指针,直到根节点或找到匹配[^2][^3]。 - 在BFS中,队列初始化时加入根节点的所有子节点。 例如,模式串{"he", "she"}的构建过程:根节点"h"和"s"的失败指针指向根;节点"e"(在"he"中)的失败指针,通过其父节点"h"的失败指针(根)查找,但根没有直接子节点"e",因此指向根[^2]。 #### 3. **代码实现** 以下代码基于引用[1]和[3]的伪代码实现,使用C++风格,但语言中立易于理解。代码包括Trie节点结构、构建失败指针和匹配函数。 **节点结构定义**: ```cpp struct Node { int cnt; // 记录模式串结尾计数(大于0表示匹配成功,-1表示已访问) Node* fail; // 失败指针 Node* son[26]; // 子节点数组(假设字符集为小写英文字母) Node() : cnt(0), fail(nullptr) { for (int i = 0; i < 26; i++) son[i] = nullptr; } }; ``` **构建失败指针(BFS遍历)**: ```cpp void buildFail(Node* root) { queue<Node*> q; for (int i = 0; i < 26; i++) { if (root->son[i]) { root->son[i]->fail = root; // 根的子节点失败指针指向根 q.push(root->son[i]); } } while (!q.empty()) { Node* curr = q.front(); q.pop(); for (int i = 0; i < 26; i++) { Node* next = curr->son[i]; if (next) { Node* temp = curr->fail; while (temp && !temp->son[i]) temp = temp->fail; // 递归查找匹配后缀 next->fail = temp ? temp->son[i] : root; // 失败指针指向匹配节点或根 q.push(next); } } } } ``` **模式匹配函数**: ```cpp int match(Node* root, string s) { int res = 0; // 匹配计数 Node* cur = root; // 从根节点开始 for (char ch : s) { int idx = ch - 'a'; // 字符映射(如小写字母) // 移动当前节点:如果子节点不存在,则跳fail while (cur != root && !cur->son[idx]) { cur = cur->fail; } if (cur->son[idx]) cur = cur->son[idx]; // 匹配成功,移动 else cur = root; // 匹配失败,回根 // 跳fail链,收集所有后缀匹配 Node* temp = cur; while (temp && temp->cnt >= 0) { res += temp->cnt; // 累加匹配计数 temp->cnt = -1; // 标记已访问,避免重复 temp = temp->fail; } } return res; } ``` **说明**: - 匹配函数遍历文本串`s`,时间复杂度接近$O(n)$。 - `cnt`字段用于标记模式串结尾:初始化为模式串数量,匹配后设为-1防止重复[^1][^3]。 - 实际应用中,可添加输出链表来记录匹配位置。 AC自动机通过Trie树和失败指针优化,实现了线性时间的多模式匹配,适用于大规模文本处理。如果您有特定编程语言需求或更多细节疑问,请提供更多信息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值