
简介自动机理论、语言和计算导论是计算机科学领域的经典教材这份中文版课后习题答案解析面向需要系统巩固有限自动机、形式语言与计算理论基础的本科生、考研学生及自学者。全书为单个PDF格式电子文档压缩包仅401KB便携易用可在多种设备上对照原书随时查阅。答案覆盖多个章节的典型习题包括确定性/非确定性有限自动机的状态构造与转移表、扩展转移函数δ-hat的归纳证明以及下推自动机与上下文无关语言相关题目既有逐步推理也有结论总结对关键证明步骤给出归纳基础与归纳假设的详细说明帮助读者不仅知道答案更能理解方法。目前已有1134人浏览下载适合作为《自动机理论、语言和计算导论》的配套练习参考用于验证解题思路、深化对核心概念的理解。1. 自动机理论、语言和计算导论课后答案这份 PDF 能帮你把题刷明白但别当标准答案背《自动机理论、语言和计算导论》的课后习题是编译原理、形式语言与自动机这门课绕不过去的坎也是很多人考研复试前临时抱佛脚的最后一根稻草。这份中文版 PDF 覆盖了原书第 2、3 章的核心题目DFA 转移表、NFA 转 DFA、ε-闭包、正则表达式与自动机互转全都给了完整推导和归纳证明拿来对答案、补推导过程都够用。适合谁准备复试的学生、正在写 C 语言词法分析器却被状态表绕晕的从业者以及所有想把自动机理论从“背概念”提升到“能做题”的人。但它不是照着抄就能过的那种答案里面既有 δ-hat 符号显示成问号的排版问题也有把接受条件译反的句子。想用它刷题先得知道哪些地方要绕着走。2. 从转移表读懂 DFA2.2 节答案的状态编码与设计套路2.2 节是整个 PDF 里信息量最大的一节也是大部分读者第一次翻车的地方。答案直接给转移表不给状态图而且状态名不是简单的 q0、q1而是像000r、010a这样的组合编码。读表第一步是先搞清楚编码每一位代表什么再去碰那张十几行的表否则连接受态都数不对。2.1 16 个状态为什么只剩 13 个先看编码再读表以 2.2.1(a) 的弹珠机题为例。三个杠杆每个杠杆有左右两种位置用三位 0/1 表示从左到右依次对应第一个、第二个、第三个杠杆后面再跟一个字母 a 或 ra 代表接受态r 代表拒绝态。所以一个完整状态是四字符比如000r表示三个杠杆都在左、当前是拒绝态*000a表示同样的杠杆位置但当前是接受态。理论上有 16 种组合答案却只列了 13 行因为从初始状态000r出发按转移边能到达的只有 13 个剩下 3 个落在不可达分支里。怎么确认“只有 13 个可达”常见做法是 BFS把000r放进队列读入 0 得到目标状态、读入 1 得到目标状态把没见过的状态继续入队直到队列为空。这个步骤不用写程序拿张草稿纸也能做但你要是想偷懒答案是现成的。完整转移表如下-标记初始状态*标记接受状态状态输入 0输入 1-000r100r011r*000a100r011r*001a101r000a010r110r001a*010a110r001a011r111r010a100r010r111r*100a010r111r101r011r100a*101a011r100a110r000a101a*110a000a101a111r001a110a数一下接受态带*的有 6 个000a、001a、010a、100a、101a、110a。这个数量别嫌多弹珠机题里三个开关不同位置都可能让球从 D 掉出接受态自然不止一个。我读这张表有个固定动作先拿笔把初始状态和所有*行圈出来再逐行看转移目标最后回到题设里核对“接受到底意味着什么”。这套动作放在任何 DFA 题上都适用顺序不能反先圈状态再对题设。2.2 模 5 DFA 的设计思路余数状态 初始约束2.2.6(a) 是另一道经典题识别二进制整数串要求被 5 整除、且串以 1 开头。答案的核心技巧很短读入一个 0相当于把已经看到的数乘 2读入一个 1相当于乘 2 再加 1。任何整数都能写成5a b的形式b 是 0 到 4 的余数。读 0 后变成2(5ab) 10a 2b10a确定是 5 的倍数所以新余数只跟2b的余数有关读 1 时同理只跟2b1的余数有关。于是状态不需要记住整个数记住余数就够这就是状态压缩的思路。中间版本的转移表长这样状态 qi 表示当前数除以 5 的余数是 i状态输入 0输入 1-*q0q0q1q1q2q3q2q4q0q3q1q2q4q3q4转移规则可以总结成一句可抄作业的话qi读 0 去q(2i mod 5)读 1 去q((2i1) mod 5)。比如 q3 读 03×266 mod 51去 q1q4 读 14×2199 mod 54留在 q4。对照表逐行验一遍全都满足。但这里有个隐蔽的边界问题这张表天然接受以 0 开头的串比如输入0余数是 0q0 又是接受态直接被接受了。题目要求只接受以 1 开头的二进制整数所以答案在完整版里补了两个状态初始状态 s 和“死亡状态”d。s 看到 1 才进入正常余数计算流程 q1看到 0 直接进 dd 上所有输入都自环永远出不来。完整表才是最终版状态输入 0输入 1-sdq1*q0q0q1q1q2q3q2q4q0q3q1q2q4q3q4ddd这个“加初始约束状态 死状态”的套路在后面的题目里反复出现。凡是题目对串开头有额外要求必须以某字符开头、不能以某字符开头纯状态自动机通常搞不定加一个 s 判断首字符再加一个 d 处理非法开头是最省事的解法。我一般遇到这类题会先在草稿上把中间表写完确认余数转移正确再回头补 s 和 d顺序反了容易把转移表改乱。3. 子集构造法实战NFA 转 DFA 的答案是怎么推出来的第 3 章答案里最容易被当成“天书”的就是 2.3 节和 2.4 节那几张用字母 A 到 H 命名的 DFA 转移表。这些字母不是随便编的每一个都对应 NFA 状态集合的一个子集。理解子集构造法比背答案重要得多因为它把“怎么从 NFA 得到 DFA”这个黑匣子彻底拆开了。3.1 A 到 H八个 DFA 状态是怎么长出来的2.3.1 的答案直接给了八个集合A {p}B {p,q}C {p,r}D {p,q,r}E {p,q,s}F {p,q,r,s}G {p,r,s}H {p,s}。注意一个规律——每个集合里都含 p因为 p 是 NFA 的初始状态子集构造法第一步永远是“求初始状态出发的 ε-闭包”闭包天然包含 p 自己。随后的每一步都是对当前集合里的每个 NFA 状态读入输入符号后取所有后继状态再对结果求 ε-闭包生成新的集合如果这个集合之前没见过就给它分配一个新的 DFA 状态字母。DFA 状态对应的 NFA 状态集合A{p}B{p,q}C{p,r}D{p,q,r}E{p,q,s}F{p,q,r,s}G{p,r,s}H{p,s}哪个集合是 DFA 的接受态只要集合里包含 NFA 的终态它就是接受态。E、F、G、H 都含 s所以答案里 E、F、G、H 全部标了*。这个判定规则很机械但也容易错我见过不少人只盯着“集合里最后一个状态”看把含 s 的集合漏标。正确做法是逐个检查集合成员别图省事只看尾巴。3.2 用 2.4.2(a) 走一遍完整的子集构造2.4.1(a) 先设计了一个 NFA用来识别 abc、abd、aacd 三种串。q1、q2、q3 负责识别 abcq4、q5、q6 负责识别 abdq7 到 q10 负责识别 aacd初始状态是 q0。接着 2.4.2(a) 对它做子集构造得到 A 到 H 八个状态完整的 DFA 转移表是四列因为输入字母表是 a、b、c、d状态abcd-ABAAABCDAACCDEADBAFGEBAAH*FBAAA*GBAAA*HBAAA注意几个细节。第一状态 A 读 a 到 BB 是 {q0,q1,q4,q7}正好是 NFA 在 q0 读入 a 后所有可能到达的状态集合。第二状态 D 读 c 到 F、读 d 到 GF 含 q3 所以代表 abc 已经被识别完G 含 q6 代表 abd 识别完。第三答案原文特意提了一句F、G、H 三个状态可以合并成一个接受态也可以用三个状态分别标记“abc 被识别”“abd 被识别”“aacd 被识别”。这里就是取舍问题。如果你只是验证语言对不对合并成一个接受态最省如果你在做词法分析想区分匹配到的是哪个关键字保留 F、G、H 更合适。实际工程里我倾向于不合并因为后续要挂不同的动作合并了还得回查状态纯给自己添堵。3.3 ε-闭包含 ε 转移时先做闭包再做子集2.5.1 专门处理带 ε 转移的 NFA。答案先算闭包p 的 ε-闭包是 {p}q 的 ε-闭包是 {p,q}r 的 ε-闭包是 {p,q,r}。为什么 q 的闭包包含 p因为 q 有一条 ε 边到 p。ε-闭包的定义就是从当前状态出发只沿 ε 边能到达的所有状态集合包含自己。算闭包的机械做法是把状态压栈弹出后查它有没有 ε 边有就入栈直到栈空。算完闭包再看转移表就顺了。答案把三个可达集合命名为 A {p}B {p,q}C {p,q,r}转移表如下状态abc-AABCBBCC*CCCC这里有个观察角度读入 a 时所有状态都不动所以 a 列全是“留在原状态”真正引起状态变化的是 b 和 c。B 读 b 到 C是因为 B 里的 q 读 b 能到 r闭包后变成 {p,q,r}C。凡是带 ε 边的 NFA做子集构造前必须先求 ε-闭包再把闭包结果当整体处理跳过这一步直接按普通 NFA 算子集结果一定少状态。4. 正则表达式与自动机互转3.1、3.2 节的两条解题主线正则表达式这一节的答案表面看是各种花式写法实际只有两条主线一是“先描述约束再加边界例外”二是“用状态消除法把自动机逆推成表达式”。两条线吃透3.1 和 3.2 的题基本都能自己推。4.1 “无相邻 1”的两类写法与消除边界项3.1.2(a) 要求写出“没有两个 1 相邻”的正则表达式答案给了(100)*(ε1)。拆开看(100)*表示若干个“10 或 0”每个 1 后面都强制跟一个 0这样永远不会出现相邻的 1末尾的(ε1)处理一种例外——整个串以单个 1 结尾这个 1 后面没有 0 可跟但因为它只有孤零零一个并不违反“相邻”约束。这就是典型的“先描述主体约束再补边界例外”思路。3.1.4(a) 给了完全不同的写法(00*1)*0*(ε1)。这个表达式的角度是倒过来的强调每个 1 前面至少有一个 0 做前缀00*1就是“一堆 0 后跟一个 1”(00*1)*重复多组末尾0*允许最后一个 1 后面还有 0开头的(ε1)允许整个串以 1 开头。两种写法描述的是同一个语言但一个按“后跟”约束一个按“前缀”约束这说明正则表达式本身不唯一别因为答案写法不同就怀疑自己。3.1.2 的后半部分更有意思。题目要求前缀无相邻 1、后缀无相邻 0答案先写出(100)*(ε1)(011)*(ε0)然后指出中间的ε1是多余的——因为后续的(011)*本身就能提供一个 1前面的边界项可以被吞掉。最终简化为(100)*(011)*(ε0)。这种“消冗余项”的动作考试时能帮你省不少字符但前提是你能确认删掉的项被其他部分覆盖删错了整个表达式就错了。4.2 状态消除法与 R^k_ij 下标怎么对着答案自查3.2.1(a) 的答案一开始就直接列了一堆 R 下标表达式比如 R11 ε1、R12 0、R13 φR21 1、R22 ε、R23 0R31 φ、R32 1、R33 ε0。这套下标表示法是定理 3.2 的产物R^k_ij表示从状态 i 到状态 j、中间只经过编号不大于 k 的状态的路径所对应的正则表达式。k0 意味着中间不经过任何状态所以 R12 0 表示从状态 1 到状态 2 有一条直接标着 0 的边R13 φ 表示没有直达路径。表达式名含义R11 ε1状态 1 的自环边标着 1加上原地不动的 εR12 0状态 1 到状态 2 的直达边标着 0R13 φ状态 1 到状态 3 没有直达路径R21 1状态 2 到状态 1 的直达边标着 1R22 ε状态 2 的自环只有 εR23 0状态 2 到状态 3 的直达边标着 0R31 φ状态 3 到状态 1 没有直达路径R32 1状态 3 到状态 2 的直达边标着 1R33 ε0状态 3 的自环标着 0加上 ε搞清楚下标再看状态消除法的四步走。第一步给原自动机加一个新的初始状态和一个新的终止状态原来的终态不再当终态第二步选一个要消除的中间状态比如从图里挑 q2第三步对所有经过 q2 的路径更新表达式基本公式是R R1 R2 R3* R4含义是从入边状态到出边状态要么走原来的直达路径 R1要么先进到 q2 的 R2、在 q2 上绕 R3 若干圈、再沿 R4 出去第四步重复直到只剩新初始状态到新终止状态这一条边。3.2.1(e) 的答案正好展示了一次完整消除过程先消 q2最终得到从 q1 到 q3 的表达式[1 01 00(010)*11]*00(010)*方括号里是留在 q1 的三种走法直接走自环 1、走 01 回来、走00(010)*11绕一圈回来方括号整体带星号表示可以任意重复最后00(010)*是从 q1 一路走到 q3 的那条路径。对着这个公式反推状态图你会发现每一步都有对应010是中间某个循环(010)*表示它可重复。我自己的习惯是拿到这种表达式先不急着背而是把方括号里的每一项还原成图上的回路还原不了就说明理解还没到位。5. 避坑照答案校对也翻车的四个典型误读这份 PDF 的答案主体是可靠的但翻译和排版留下的坑不少。下面四条是我和周围人实际踩过的按“现象 → 原因 → 解决”写清楚你刷题时碰到类似情况能少走弯路。5.1 符号与格式类δ-hat 乱码、*终态标记认错坑一PDF 里 δ-hat 全部变成“?”。现象第 2 章习题答案里到处都是?(q,x)、?(q,xz)这样的式子读题时完全不知道这个“?”是什么运算符。原因这份 PDF 是从网页转制而来数学符号的字体映射丢了所有 δ-hat扩展转移函数被统一替换成了问号字符。解决先把 PDF 转成纯文本定位这些位置再按上下文还原成 δ-hat。转文本的命令是pdftotext -layout automata-answers.pdf answers.txt grep -nF ? answers.txt | head -20-layout参数保持版面顺序grep -nF里的-F把问号当普通字符而不是正则通配符head -20只显示前 20 行定位关键位置。转完文本你就会发现凡是出现在式子里的“?”都是 δ-hat凡是出现在“原因”栏里的问号才是真问号。坑二转移表里的*和-约定读漏。现象对着 2.2.1(a) 的表抄状态把带*的接受态当成普通状态最后验证“哪些串被接受”时怎么都对不上。原因教材约定-只标记初始状态*标记接受状态但答案的表格排版紧凑两个符号都贴在状态名左侧扫一眼很容易漏。解决读任何一张转移表之前先做两件事把-所在行圈出来把带*的行单独抄到草稿纸上。这两行就是自动机的“入口”和“出口”漏掉任何一个后面全白算。5.2 语义与边界类接受条件被译反、前导 0 没处理坑三2.2.10 的中文翻译把接受条件写反了。现象中文版答案写“不管串有偶数个还是奇数个 1, 都会被接受”但状态 A 表示偶数个 1、B 表示奇数个 1转移表里只有 B 是接受态实际应该只接受奇数个 1 的串。原因翻译时把原英文accepting in the latter case理解成“两种情况都接受”其实后者特指“奇数个 1”那种情况。解决拿状态名反推A 是偶数、B 是奇数、只有 B 带*结论一目了然——凡是中文描述和转移表对不齐一律以转移表为准别信翻译。坑四2.2.6(a) 的中间版本没处理前导 0。现象直接用 q0 到 q4 那张模 5 表去判断二进制串发现0、00这些明显不该被接受的串全被接受了。原因纯余数自动机只看余数不关心串是否以 1 开头而题目要求只接受以 1 开头的整数串。解决把答案里“补充 s 和 d 状态”的那张完整表抄下来用。s 看到 1 进 q1 开始正常计算看到 0 直接进 d 死状态d 对所有输入自环进去就再也出不来了。这个边界条件很容易在看书时忽略因为中间表和完整表长得很像只差两行但这两行恰好是题目的核心约束。6. 一个验证答案的具体习惯状态图重画 小脚本校对刷这份答案到后期我养成一个固定习惯每做完一道 DFA 题先不急着看 PDF 里的转移表而是自己画状态图再拿表核对。画图的好处是能发现表格里看不出来的问题比如状态跳转是否符合直觉、接受态是不是真的在“该接受的路径”上。2.2.10 那道题画完图你会立刻发现 A 到 B、B 到 A 来回跳对应“1 的个数奇偶切换”只有 B 带圈接受条件天然是奇数个 1。光画图还不够我一般还会写个小脚本模拟 DFA用边界用例批量验一遍。下面这段 Python 代码就是通用的 DFA 模拟器改转移表就能套用到任意题目# 转移表用字典表示状态 - {输入符号 - 下一状态} delta { A: {0: A, 1: B}, # A偶数个 1 B: {0: B, 1: A}, # B奇数个 1 } start A accept {B} def run(w): s start for ch in w: s delta[s][ch] # 逐字符查转移表 return s in accept # 边界用例空串、单个1、两个1、三个1 for w in [, 1, 11, 101, 1101]: print(repr(w), run(w))代码逻辑很直白run从初始状态出发逐个字符查delta字典走完整个串后判断当前状态是否在accept集合里。repr(w)的作用是让空串显示成避免输出里一个空白什么都看不出来。跑一次结果空串不接受、1接受、11不接受、101接受、1101接受——完全符合“奇数个 1 被接受”也反过来验证了坑三里说的中文翻译确实是错的。把 2.2.1(a) 那 13 行表也改成这种字典格式同样能跑遇到大表还能省去手算的枯燥。从那以后我每次拿到这类中文版答案都强制走一遍“重画状态图再对照文本”的流程。画完图、跑完脚本才允许自己往笔记上抄转移表。那些翻译痕迹明显、符号乱码的段落我甚至会直接在 PDF 空白处标注修正内容免得下次复习时再踩一遍。这份答案是个好工具但前提是你比它更严谨。希望帮到你。本文还有配套的精品资源点击获取