有限状态自动机理论
大部分内容实际上就是把论文抄了一遍,有些地方补充了一些东西。
下文中 \(\sum\) 指字符集,\(\sum^{*}\) 指在 \(\sum\) 上的所有串,\(\sum_{\epsilon}=\sum\cup\{\epsilon\}\),\(P(Q)\) 表示 \(Q\) 的幂集。
用 \((Q,\sum,\delta,q_0,F)\) 描述一个确定性有限状态自动机(DFA):
- \(Q\):有限状态集合。
- \(\sum\):有限字符集。
- \(\delta:Q\times\sum\to Q\):转移函数。
- \(q_0\in Q\):起始状态。
- \(F\):接受状态集合。
对于 DFA \(M=(Q,\sum,\delta,q_0,F)\) 和 \(w\in\sum^{*}\),称 \(M\) 接受 \(w\) 当存在状态序列 \((r_0,r_1,\dots,r_n)\) 使得:
- \(r_0=q_0\)。
- \(\delta(r_i,w_i)=r_{i+1}\)。
- \(r_{n-1}\in F\)。
形式语言(语言)是 \(\sum^{*}\) 的一个可空子集。对于所有 \(M\) 接受的串 \(w\) 构成的集合 \(L(M)\),称 \(M\) 识别 \(L(M)\)。能被某个 DFA 识别的语言称为正则语言。将 \(M\) 判断是否接受 \(w\) 的过程称为计算。
用 \((Q,\sum,\delta,q_0,F)\) 描述一个非确定性有限状态自动机(NFA):
- \(Q\):有限状态集合。
- \(\sum\):有限字符集。
- \(\delta:Q\times\sum_{\epsilon}\to P(Q)\):转移函数。
- \(q_0\in Q\):起始状态。
- \(F\subset Q\):接受状态集合。
\(\epsilon\) 在串中可以任意增删。NFA \(M=(Q,\sum,\delta,q_0,F)\) 接受 \(w\) 当存在状态序列 \((r_0,r_1,\dots,r_n)\) 使得:
- \(r_0=q_0\)。
- \(r_{i+1}\in\delta(r_i,w_i)\)。
- \(r_{n}\in F\)。
NFA 能识别的语言类显然不会少于 DFA。可以构造地证明,NFA 和 DFA 识别语言的能力相同。称两个有限状态自动机(FSM)等价,当它们能识别的语言类相同。对于 NFA \(M=(Q,\sum,\delta,q_0,F)\),令 \(E(q)\) 代表从 \(q\) 出发,只沿 \(\epsilon\) 转移能到达的状态集合,构造 DFA \(M'=(Q',\sum,\delta',E(q_0),F')\):
- \(Q'=P(Q)\)。
- \(\delta':Q'\times\sum\to Q'\),其中 \(\delta'(S,c)=\cup_{s\in S,q\in\delta(s,c)}E(q)\)。
- \(F'=\{S|S\subset Q,S\cup F\neq\emptyset\}\)。
容易发现 \(M'\) 所在的状态就是 \(M\) 所在的状态集合。由于 DFA 和 NFA 计算能力等价,因此一个语言是正则的当且仅当它能被某个 NFA 识别。
除了用 FSM 表达正则语言外,另一种方法是正则表达式,称 \(L(R)\) 表示正则表达式 \(R\) 对应的语言,\(R\) 可以是:
- \(c\in\sum\),对应 \(L(R)=\{c\}\)。
- \(\epsilon\),对应 \(L(R)=\{\epsilon\}\)。
- \(\emptyset\),对应 \(L(R)=\emptyset\)。
- \((R_1+R_2)\),对应 \(L(R)=L(R_1)\cup L(R_2)\)。
- \((R_1R_2)\),对应 \(L(R)=\{uv|u\in L(R_1),v\in L(R_2)\}\)。
- \((R_1^{*})\),对应可数个 \(L(R_1)\) 中的元素拼接,即 \(L(R)=\{u_1u_2\dots u_n|u_i\in L(R_1),n\in\mathbb{N}\}\cup\{\epsilon\}\)。
正则表达式和 FSM 能表述的语言类相同:
一个语言是正则的,当且仅当它能被某个正则表达式表示。
从两个方向证明:
如果一个语言能被正则表达式表达,那么它是正则的。
只需将正则表达式构造成一个 NFA。利用正则表达式的生成方式进行归纳构造。图示可见徐哲安的集训队论文。
如果一个语言是正则的,那么它可以被某个正则表达式表示。
即找到一种将 FSM 转化成正则表达式的方法,由于 NFA 和 DFA 等价且 DFA 结构较为简单,因此只需找到将 DFA 转化成正则表达式的方法。首先引入广义非确定性有限状态自动机(GNFA),用一个五元组 \((Q,\sum,\delta,q_s,q_t)\) 描述:
- \(Q\) 是有限状态集合。
- \(\sum\) 是有限字符集。
- \(\delta:(Q/\{q_t\})\times(Q/\{q_s\})\to R\),每条边上是一个正则表达式。
- \(q_s\in Q\):开始状态。
- \(q_t\in Q\):接受状态。
\(w=w_0w_1\dots w_{n-1}\) 被某个 GNFA \(M=(Q,\sum,\delta,q_s,q_t)\) 接受,当存在状态序列 \(q_0q_1\dots q_m\):
- \(w\) 可以写成 \(w_0w_1w_2\dots w_{m-1}\)(\(w_i\in\sum^{*}\)),\(w_i\in L(\delta(q_{i},q_{i+1}))\)。
- \(q_0=q_s\)。
- \(q_m=q_t\)。
我们可以将一个 DFA 转化成 GNFA。对于 DFA \(M=(Q,\sum,\delta,q_0,F)\),构造 GNFA \(M'=(Q\cup\{q_s\}\cup\{q_t\},\sum,\delta',q_s,q_t)\),其中:
- \(\delta'(q_s,q_0)=\epsilon\),\(\delta'(f\in F,q_t)=\epsilon\)。
- 对于其它转移,可以直接使用正则表达式达到相同的效果。
只需说明 GNFA 可以转换为正则表达式。考虑一个拥有 \(k\) 个状态的 GNFA(\(k>2\)),任取 \(q_r\in Q/\{q_t\}/\{q_s\}\),构造新的 GNFA \((Q/\{q_r\},\sum,\delta',q_s,q_t)\)。其中对于 \(q_i\in Q/\{q_t\},q_j\in Q/\{q_s\}\),有:
这个构造凸显了正则表达式的强大之处。新的 GNFA 与原来的 GNFA 等价,因此我们找到了一种将 GNFA 状态数减少的有效方法。直到 \(k=2\) 时,连接 \(q_s\) 和 \(q_t\) 的正则表达式就与整个 GNFA 等价。
正则表达式提供了一种从代数角度描述正则语言的方法,以下关于正则表达式的代数定律成立:
- \(L+M=M+L\)。
- \((L+M)+N=L+(M+N)\)。
- \((LM)N=L(MN)\)。
- \(\emptyset+L=L\),即空集是并运算的单位元(以下也称加法运算)。
- \(\epsilon L=L\),即 \(\epsilon\) 是连接运算的单位元(以下也称乘法运算)。
- \(\emptyset L=\emptyset\),即空集是乘法运算的零元。
- \(L(M+N)=LM+LN\)。
- \(L+L=L\)。
- \((L^{*})^{*}=L,\epsilon^{*}=\epsilon,\emptyset^{*}=\epsilon\)。
乘法没有交换律,正则表达式除了没有逆外,整个结构都非常类似一个环。定律中所描述的正则表达式都是以变量的形式给出的,此法在抽象代数中运用是顺理成章的,但是在正则表达式中,用变量还是存在若干不便之处,例如如何判断两个带变量的正则表达式是否相同?抽象代数中我们判断两个带变量的元素相同,只需比较其的形式是否等价,正则表达式中基于如下引理也存在类似的方法:
设 \(E\) 是包含变量 \(L_1,L_2,\dots,L_m\) 的正则表达式,对于每个 \(L_i\) 将其替换为标志符 \(a_i\),得到形式正则表达式 \(T(E)\)。若将每个 \(L_i\) 替换为具体的语言 \(L'_i\),得到具体的正则表达式 \(E'\)。我们可以构造 \(L(E')\):对于任意 \(A\in L(T(E))\),将其中的 \(a_i\) 替换为 \(L'_i\) 中的任意元素,将所有得到的串加入 \(L(E')\),记这些串的集合为 \(w(A)\)。
需要证明的无非就是 \(w(A)\subseteq L(E')\),且 \(L(E')\) 中的每个串都在某个 \(w(A)\) 出现过。归纳证明,此处略过。注意若正则表达式引入交运算,则引理不再成立。根据引理可以得到:
对于一对带有相同变量集合的正则表达式 \(E\) 和 \(F\),\(E=F\) 当且仅当 \(L(T(E))=L(T(F))\)。
这一部分后面再写。
一点用没有,不写了。
设 \(L\) 是正则语言,存在与 \(L\) 相关的常数 \(n\),使得对于 \(L\) 中的串 \(|xyz|\ge n\),满足:
- \(y\neq\epsilon\)。
- \(|xy|\leq n\)。
- \(xy^kz\in L\),其中 \(k\geq 0\)。
考虑 \(L\) 对应的一个 DFA,设该 DFA 有 \(n\) 个状态,对于 \(L\) 中长为 \(m\geq n\) 的串 \(w=w_1w_2\dots w_m\) 而言,令 \(p_i\) 为读入 \(w_i\) 后在 DFA 上所处的状态。根据抽屉原理,必然有 \(0\le i<j\le n\) 满足 \(p_i=p_j\),因此构造:
- \(x=w_1w_2\dots w_i\)。
- \(y=w_{i+1}w_{i+1}\dots w_j\)。
- \(z=w_{j+1}w_{j+2}\dots w_m\)。
其中 \(y\) 在 DFA 上形成了一个环,因此 \(xy^kz\in L\)。这称作正则语言的泵引理,泵引理为判断正则语言提供了必要条件(并非充要)。
两个 DFA 等价当且仅当识别相同的正则语言,所有 DFA 被划分为了无穷个等价类 ,我们往往只关心状态数最小的那个(最小 DFA)。
对于某个 DFA,在两个状态 \(pq\) 之间定义关系 \(\sim\),\(p\sim q\) 当且仅当对于任意输入串 \(w\),令 \(\delta'(p,w)\) 代表以 \(p\) 为初始状态时读入 \(w\) 后所在的状态,则 \([\delta'(p,w)\in F]=[\delta'(q,w)\in F]\)。即不存在输入串能区分 \(p\) 和 \(q\)。注意 \(w\) 可以为 \(\emptyset\)。
\(\sim\) 是等价关系。将 DFA 的所有状态划分成了若干个等价类 \(S_1,S_2,\dots,S_m\),考虑若对于 \(p\in S_i\),有 \(\delta(p,c)\in S_j\),则 \(\forall p'\in S_i,\delta(p',c)\in S_j\)。只需考虑反证,若存在 \(q\in S_i\) 使得 \(\delta(p,c)\in S_k(j\neq k)\),任取一个可以区分 \(S_j\) 和 \(S_k\) 的串 \(w\),则我们取 \(c+w\) 即可区分 \(pq\)。
同样,一个等价类里面要么全是接受状态,要么全不是接受状态,考虑取 \(w=\emptyset\) 即可说明。设原来的 DFA 为 \(A\),我们新建一个 \(B\),使得 \(B\) 的状态为 \(A\) 的所有等价类,转移函数和接受状态依上文简单得到。取 \(A\) 的初始状态所在的等价类为 \(B\) 的初始状态。可以说明,\(B\) 和 \(A\) 等价。考虑任取串 \(w\),在同一时刻,在 \(A\) 上的状态所属等价类即为在 \(B\) 上的状态,因此两者等价。
我们可以通过不断分裂等价类来减少 DFA 的状态数。令 \(p\sim_k q\) 代表只考虑所有长度不超过 \(k\) 的串所带来的等价关系。维护 \(G_k\) 代表 \(\sim_k\) 带来的所有等价类,于是 \(G_k\to G_{k+1}\) 只需要对每个等价类内部做分裂。对于在 \(G_k\) 中属于同一等价类的 \(u\) 和 \(v\),其在 \(G_{k+1}\) 中不属于同一等价类当且仅当 \(\delta(u,c)\) 和 \(\delta(v,c)\) 在 \(G_k\) 中不属于同一等价类。具体实现只需哈希维护,单轮分裂时间复杂度 \(O(n|\sum|)\),迭代次数最坏为 \(O(n)\)。不过,随机情况下迭代次数可能达到 \(O(\log n)\) 甚至更低。
为了说明此时的 DFA 就是最小的 DFA,我们介绍 Myhill-Nerode 定理。
对于给定的语言 \(L\),以及任意一对串 \(xy\),定义 \(x\equiv_L y\) 当且仅当 \(\forall z,xz\in L\Leftrightarrow yz\in L\),易见这是一个等价关系,将 \(\sum^{*}\) 划分成了若干等价类。Myhill-Nerode 定理指出:
- 一个语言 \(L\) 是正则的,当且仅当 \(\equiv_L\) 对应的等价类个数有限。
- 描述正则语言 \(L\) 的最小 DFA 唯一(忽略状态标号),状态数为 \(\equiv_L\) 对应的等价类个数。
分作了两个方向证明。
对于正则语言 \(L\),都有 \(\equiv_L\) 对应的等价类个数有限,且不超过识别 \(L\) 的最小 DFA 的状态数。
取一个识别 \(L\) 的最小 DFA,若 \(\equiv_L\) 的等价类数量超过该 DFA 的状态数,则根据抽屉原理,必然有两个不属于同一等价类的串到达 DFA 的同一状态。
对于任意语言 \(L\),若有 \(\equiv_L\) 对应的等价类个数有限,则一定能构造出识别其的唯一的 DFA 使得状态数等于等价类个数。
类似上文关于 DFA 上等价关系的构造。易有对于 \(x\equiv_L y\),任取字符 \(c\),有 \(xc\equiv_L yc\),证明同上。同时存在一个等价类为 \(L\)。那么我们构造一个等价类和状态一一对应的 DFA,取空串所在等价类为初始状态,取 \(L\) 为接受状态,转移函数由等价类之间的转移关系得到。考虑说明 DFA 的唯一性,若有一个和 \(A\) 不同构,状态数等于等价类个数的 DFA \(B\),则 \(B\) 的状态也需要和等价类一一对应,容易发现必然会存在串在 \(B\) 上转移时被划分到了错误的等价类中。
DFA 上的不同等价类意味着不存在输入串能区分两个状态,\(\sum^{*}\) 上的不同等价类意味着不存在 \(z\) 能区分 \(xy\)。最小化后的 DFA 状态显然一一对应所有等价类,因此合并等价类后的 DFA 就是最小 DFA。Myhill-Nerode 定理也给出了一种判断正则语言的方法,若存在任意多个串两两不等价,则 \(L\) 不是正则语言。
Hopcroft 算法基于启发式分裂来最小化 DFA。考虑暴力的分裂算法中实际上在对于等价类 \(Y,A\),找到沿着 \(c\) 能转移到 \(A\) 的状态集合 \(X\),若 \(Y\cap X\) 和 \(Y/X\) 均非空,则将 \(Y\) 分裂成两部分。\(A\) 即为分裂其它等价类所用的证据。考虑维护所有证据 \(A\),以及对应的 \(X\),每次找到满足条件的 \(Y\) 分裂成 \(U\) 和 \(V\)。此时的 \(Y\) 有两种情况,一种是 \(Y\) 还未作为证据使用,此时 \(U\) 和 \(V\) 共同作用可以替代 \(Y\);若 \(Y\) 已经作为证据使用,可以证明 \(U\) 和 \(V\) 任取一个作为证据即可,我们取大小较小的那个作为证据。
一个状态最多 \(O(\log n)\) 次被作为证据使用,因此若能快速处理 \(Y\),时间复杂度为 \(O(n|\sum|\log n)\)。考虑找出 \(X\) 后,枚举所有与其有交的等价类 \(Y\),若 \(|Y\cap X|\) 较小,则将 \(|Y\cap X|\) 单独取出,原集合 \(Y\) 不变,下一次访问到的时候重构。否则意味着暴力处理 \(Y\) 的时间复杂度至多为 \(O(|X|)\),总时间复杂度 \(O(n|\sum|\log n)\)。对于随机生成的 DFA,时间复杂度能达到 \(O(n|\sum|\log\log n)\)。
研究 DFA 在 OI 中的应用。
我曾经认为 DFA 在 OI 中没有任何应用,直到我没有做出来 abc 的 DFA 题。
作者好菜啊,学过的东西都做不出来,听说就这个水平还在天天摆烂,这种人要是进省队是对 OI 的不尊重。
GYM102586J
这题是 AGC022E 的加强版,将连续三个字符替换为某个字符,问是否能变成 \(1\)。
首先声称,所有好的字符串构成了一个正则语言,证明这个东西是很困难的,但是我们可以证明一个弱一点的东西,所有长度不超过 \(B\) 的好的字符串构成了一个正则语言,其对应的最小 DFA 状态数不超过 \(b\),根据 Myhill-Nerode 显然成立(等价类只有有限个)。
接下来我们完全地从 DFA 和 Myhill-Nerode 的角度去考虑问题。考虑这个 \(b\),直接找到 \(b\) 不容易,但是判定 \(b\) 是容易的,找到所有长度 \(\leq b\) 的串构成的所有等价类,类似最小化 DFA 的过程,若 \(b+1\) 对应的等价类和 \(b\) 的等价类同构,那么 \(b\) 就是我们需要的。
在这个题目中 \(b\) 并不大,于是自动机的状态数也不大,那么只需要对不超过 \(b\) 的串按照等价关系合并和建出转移函数即可。看上去等价关系很难判断,但是我们有泵引理,利用泵引理只需检查总长不超过 \(b\) 的串,看上去非常优美啊!
实际上 \(b\) 取一个看上去很对的数即可。
P12294 [THUPC 2025 决赛] 一个 01 串,n 次三目运算符,最后值为 1(加强版)
多了一个构造。