☰
编译原理第三章习题答案详解:词法分析、正规式与DFA构造
2026/10/12 21:03:15 网站建设 项目流程

简介:一份面向编译原理学习者的PDF资料,对应蒋立源《编译原理》(第三版)第三章习题与答案解析,聚焦文法、状态转换图、NFA与DFA、正规式等编译前端核心知识点。资源为单个PDF文件,大小约2.97MB,内容完整覆盖第三章从习题3-1到3-8的题目与推导过程,既有原题剖析,也有逐步推导与答案整理,适合计算机专业学生课后巩固、考研复习或教师备课参考。目前已有1919人学习下载。解答部分细致展示了右线性文法等价构造、状态转换图与状态转换矩阵的互化、不同初终态设定下的3型文法描述、NFA确定化与最小化、含ε动作的NFA处理、以及利用正规式描述文法语言等关键步骤,并配有改编后的状态命名与转换矩阵,便于逐行对照。特别适合自学时对照教材逐题演练,也可作为期末冲刺的速查手册。通过系统完成这些典型练习,读者能更扎实地掌握有限自动机与形式语言的基础理论,为后续词法分析程序设计与语法分析学习扫清障碍。

1. 一份习题答案PDF,为什么值得专门读第三遍

期末周或考研复试着实被第三章卡住的人,多半都搜过“编译原理第三章习题答案”,而蒋立源版《编译原理》第三版的使用面很广,这份标题带“修改后”的第三章习题与答案PDF,正好对准词法分析这个分水岭章节。第三章不只是讲“什么是正规式”,它把前面形式语言的基础变成了可操作的构造算法:正规式转NFA、NFA转DFA、DFA最小化,后面语法分析、语义分析全都建立在“能不能把语言变成一个确定的有穷自动机”之上。这份答案适合三类人:刷题卡推导细节的、想核对旧版笔记里错误步骤的、正在写词法分析实验需要一个对照标准的人。它的价值不在于让你背结论,而在于让你复算。

2. 蒋立源版第三章到底在考什么:词法分析、正规式与有穷自动机

2.1 从正规式到DFA的四段路:构造、去不确定、化简、运行

第三章的题型翻来覆去只有四类,但每类的答案写法差异巨大。第一类是“给定正规式构造NFA”,考的是Thompson构造法,要求把原子状态图按并、连接、闭包三种运算组合起来,正确答案必须表现出明确的初态、终态,以及闭包运算引入的空转移。第二类是“把NFA转成DFA”,用的是子集构造法,输出是一张以状态集合为行的转移表。第三类是“化简DFA”,用的是划分法,输出是一组合并后的等价状态。第四类不常单独出现,但实验和考研里很常见:给出一个DFA或NFA,要求判断某个字符串是否被接受,这本质上是状态转移表的模拟运行。

把这四类归档之后再看答案,思路会清楚很多。构造题判断对错看“每步是否合法”,化简题看“终态是否被误合并”,等价性证明题看“每一步用了哪条代数定律”。一份答案不可能只靠背就变成能力,第三遍刷的时候,我习惯先把每道题的题干关键词写到草稿纸上,再判断它属于哪一类,最后才翻PDF。这样可以避免只看答案的结构,却不知道作者在回答哪个问题。文件名里没有说明是哪道题,但答案的每页开头一般会有题目关键词,正好用来做分类索引。

2.2 为什么“修改后”三个字很重要

答案类PDF最常见的问题不是思路错,而是推导跳步和状态命名不统一。原始版本里经常出现“由上可得”这种黑匣子式写法,状态编号写到一半换了命名,表格的行列错位。标题里的“修改后”,通常意味着这些硬伤被修过一遍,所以它比普通流传版本更适合作对照。但修改后的文档也可能引入新的印刷错误,这一点很多人会忽略。

使用这份答案时要先做版本确认:手上的教材是蒋立源主编、机械工业出版社的《编译原理(第三版)》第三版,还是王生原等主编、清华大学出版社的《编译原理(第3版)》?这两本书在电商和习题检索里经常混在一起,第三章的习题编号完全不同,用错答案比不用答案还糟。检索“编译原理第3版课后习题答案第三章”时,会出现很多同名PDF,确认主编姓名是最快的分辨方式。版本一致后,还要注意印次差异,第三版的某些印次调整过习题顺序,题号对不上时以题目关键词为准。

2.3 三类典型题的对错标准不一样

构造题看步骤是否完整。答案如果直接给出最终状态图,中间少了并运算的临时状态,你就要在复算时自己补出来。NFA转DFA题看是否漏了空闭包,凡是某个新状态集合只用了一次字符转移、没有回头求闭包的,基本都要打问号。化简题看终态与非终态在第一步分组时有没有被分开,这是最容易被错误答案带偏的地方。

等价性证明题则要看两条:一是每一步变换是否只使用了正规式代数里的合法定律,比如交换律、结合律、分配律、幂等律;二是结论是否真的回到“两个正规式接受相同语言”这个含义上。答案里如果用了“显然”“同理可得”这类词,正是需要你停下来手推的地方。刷题时按这四类标准去检查,而不是按题号顺序抄,才能把PDF里的答案变成自己的判断力。第三章在期末考试和考研里占的分值不低,但真正考的不是背答案,而是给一个没见过的正规式,照样能推出最小DFA。

3. 对着答案复算一遍:把PDF里的推导还原成可验证的步骤

3.1 核对答案前的准备:版本一致性与题号标注

我拿到一份课后习题答案PDF,不会直接从头翻到尾,而是先做三件准备工作。第一,确认教材主编和版次,书名页上有蒋立源、第三版字样才算数;第二,把第三章里每道题的关键词抄到一张纸上,比如“构造NFA”“求DFA”“化简”,不抄题号只抄关键词;第三,先闭卷做一遍,再打开PDF对照。顺序反过来的话,很容易被答案的推导带着走,最后变成“貌似看懂了”而不是“真的会了”。

准备阶段还需要检查你手上的笔记版本。如果你用的是上一届流传的课堂笔记,里面可能沿用了旧版答案的错误,和这份“修改后”PDF冲突时,不要急着改笔记,先基于教材独立推一遍。我会准备两种颜色的笔:黑色写自己的推导,红色标注和PDF答案不一致的地方。这些红笔标注,往往就是你和网传答案之间的真正分界线,也是后面复习时最值得反复看的部分。版本确认无误后再进入复算,效率会高很多。

3.2 复算NFA构造:画出状态图再查表

正规式转NFA的题目,答案里通常直接给出状态图。复算时要盯住三个易错点:闭包运算是否增加了空转移边,连接运算是否让前一个自动机的终态变成了后一个自动机的初态,并运算是否引入了新的初态和终态。以正规式a(b|c)*d为例,第一步要画出四个原子自动机:a、b、c、d,然后处理连接和闭包。b和c先并成一个选择结构,接着对这个选择结果做星号闭包,最后和a、d串起来。

运算类型图形操作要点常见错误
原子正规式每个字符对应一个初态到终态的边忘记状态编号
选择新初态同时指向两个子自动机,新终态接收两路遗漏其中一个分支
连接前一个终态作为后一个初态的前驱初态和终态混淆
闭包增加空转移形成回路,新增初态与终态空转移边漏画

对照答案时,我一般先把答案里的状态图翻译成一张边表,每条边写成“状态编号,输入字符,目标状态编号”的三元组。比如某个状态图里有2, ε, 3,说明2号状态有一条空转移边指向3号。把整张图翻译成边表后,再和自己画的状态图逐条比对,很快就能找出少画或多画的那条边。答案状态编号和你的编号不同没关系,只要边的相对关系一致就说明推导等价。

3.3 复算子集构造法:状态集合与转移表

NFA转DFA是第三章里最机械、也最容易算错的一步,答案里常见“I_0 = ε-closure({x})”这类简写。复算时把每个简写都展开成完整的状态集合,再用一张三列的表逐步推进:新状态编号、当前状态集合、读入字符后到达的状态集合。以教材里常见的(a|b)*abb为例,初始状态是1号,它的 ε-closure 通常包含1号自身和经空转移可达的若干状态,记作集合A。

新状态状态集合ab
A{1, 2, 4}BC
B{2, 3, 4}BD
C{2, 4}BC
D{5}--

这张表的意思是:从集合A读入a,经过NFA的a转移和随后的空闭包,到达集合B;读入b则到达C。每次计算都要先走字符边,再求ε-closure,两个动作缺一不可。答案里如果跳过了某个集合的计算,你就得自己把缺失的行补上。表完成后,包含原始初态的集合就是DFA初态,包含原NFA终态的集合就是DFA终态。注意D是终态行,读入b后到达的集合里含有原终态5,因此D行对应接受状态。

3.4 复算DFA化简:分组细化再合并

DFA化简题的答案通常很简短,但中间过程最容易踩坑。复算时严格按三步走:第一步,把DFA的状态分成终态组和非终态组;第二步,看组内每个状态读入某个字符后落到哪一组,如果同组内两个状态落点不同,立刻拆分;第三步,反复拆分直到组内不再变化,每个组保留一个代表状态。答案里出现的“状态1和状态3可合并”,背后的含义是它们对任意输入串都表现出相同的行为。

我对照答案时有个习惯:不用答案的分组顺序,而是自己重新分一次。因为分组顺序不同,代表状态的编号会不同,但合并后的状态数和最终语言必须一致。如果你算出来的最小DFA状态数比答案多,多半是漏了某个等价合并;如果比答案少,则很可能把不该合并的状态合并了,尤其是终态和非终态混在一起的情况。验证是否合并错,可以取一个语言边界上的字符串,比如空串或单个字符,分别跑两个DFA,结果不一致就说明化简有问题。

3.5 正规式等价性证明怎么检查

等价性证明题在答案里最坑,因为它经常只写结论和关键变换,省略中间的代数定律。检查时先看每一步是否由一条已知定律支撑:(a|b)* = (b|a)*依据的是并运算的交换律,(ab)*a = a(ba)*依据的是结合律的推广。答案若出现“显然等价”,你需要自己补出中间的DFA或代数推导。另一种可靠方法是把两个正规式分别转成最小DFA,比较状态图和接收集合是否同构,不过考试时通常没有这个时间,平时练习倒是很值得做。

4. 对着这份答案复习词法分析时容易踩的坑:现象、原因与解决

4.1 漏算空闭包,子集构造法第一步就翻车

现象:复算NFA转DFA时,你的状态集合比答案少一个,或者转移表里某行顺不下去。 原因:计算每个新集合时,只走了字符转移边,没有回过头求ε-closure。空闭包既包含状态自身,也包含所有经由空转移能够到达的状态,漏掉任何一个后继状态都会改变DFA结构。 解决:每次得到字符转移后的临时集合,都强制再做一次ε-closure,并单独写一行“闭包后集合”。坚持三次以上,这个动作就会变成肌肉记忆。

4.2 化简时把终态和非终态合并了

现象:化简后的DFA能接受的字符串集合和答案不一致,比如不能接受空串。 原因:划分法的第一步分错了组,把终态和非终态放进了同一组,后续所有分裂都建立在错误分组上。 解决:第一步永远先分成两组:含终态的一组,不含终态的一组。组内再分裂时,每次都要写清楚“该状态读入a后落入第几组”。如果不确定某个合并是否合法,找到这两个状态在语言上的一个分界串,用第5章的模拟器跑一遍,结果立刻见分晓。

4.3 题号对不上:不同印次的习题顺序有调整

现象:你翻到PDF里标注的第3.7题,发现和教材上的第3.7题完全不是一回事。 原因:教材在不同印次里调整过习题顺序,或者题库排版重新编号过,而答案PDF基于某一固定印次生成。 解决:彻底抛弃题号索引,改用题干关键词定位。在教材上把“构造NFA”“证明等价”这类词标出来,再在PDF里找相同关键词的段落。靠关键词做映射,比在题号上死磕省时间得多。

4.4 “修改后”改了什么你不清楚,旧笔记和答案打架

现象:课堂笔记推导结果和这份修改后PDF不一致,你不知道该信哪个。 原因:修改后版本可能修正了原版答案的跳步或错误,也可能只是换了状态命名;旧笔记则可能沿用了网上流传的错误推导。 解决:谁对谁错不由版本新旧决定,先把教材对应定理翻出来,独立推一遍,再回头对比两份材料。我会在笔记边缘写“旧版结论+新版修改+教材依据”三行字,这样下次复习时不用重新争论。千万不要因为PDF写“修改后”就直接覆盖自己的推导过程。

4.5 把正规式的优先级当成普通运算顺序

现象:写词法规则时把a|bc理解为(a|b)c,或者把ab*理解为(ab)*,结果DFA完全变形。 原因:正规式优先级规定闭包运算最高,连接次之,选择最低。没有显式括号时,ab*只能解析为a(b*),而不是(ab)*。 解决:复算答案前,先给正规式按优先级补全括号,再开始构造NFA。一个不算技巧的技巧:考试时多写冗余括号,判卷老师不会因为括号多扣分,但会因为你漏掉优先级含义直接判错。

5. 怎么验证你从答案里学会的做法是对的:实验与自检

5.1 手写一个极简DFA模拟器验证结果

刷完一份答案后最该做的事,是把PDF里的DFA翻译成可运行的代码,用一组边界字符串检验它。常见做法是写一个几十行的模拟器,这里给出最小实现:

def run_dfa(trans, start, accepts, s): # trans: 字典,键为(当前状态, 输入字符),值为目标状态 # start: 初态,accepts: 终态集合,s: 待测字符串 state = start for ch in s: if (state, ch) not in trans: return False state = trans[(state, ch)] return state in accepts # 以(a|b)*abb的最小DFA为例 t = {("A","a"):"B", ("A","b"):"C", ("B","a"):"B", ("B","b"):"D", ("C","a"):"B", ("C","b"):"C", ("D","a"):"B", ("D","b"):"C"} print(run_dfa(t, "A", {"D"}, "abb")) # 应该接受 print(run_dfa(t, "A", {"D"}, "ab")) # 应该拒绝

逻辑说明:状态转移表被编码成字典,run_dfa沿着输入串逐字符推进,一旦某个(状态, 字符)组合在表中不存在,直接返回假。整个字符串走完后,看最终状态是否在终态集合里。参数start对应DFA初态,accepts是包含所有终态的集合,s是你要测的字符串。(a|b)*abb这个例子中,“abb”应当被接受,“ab”应当被拒绝,符合肉眼判断。

这里有个使用原则:模拟器只用来验证答案,不是用来替代手算。如果先跑代码再看推导,你只能得到对错,得不到方法。我会先手算子集构造法得到转移表,再原样翻译成字典,最后用模拟器跑几个边界串:空串、纯a串、以abb结尾的长串。这样既能确认答案的DFA正确,也能确认自己抄表时没有抄错。

5.2 用编译原理实验的方式反向验证第三章

很多人是在编译原理实验课上被词法分析器折磨时,才重新翻开第三章答案的。常见实验要求是识别标识符、无符号数和关键字,用Java实现一个能输出token序列的程序。这里头有不少学校的实验文档用的是“Java+手工状态转移表”的路子,广州大学等高校的课程实验里也能看到类似要求:把词法规则转成DFA,再用二维数组驱动识别。和5.1的字典模拟器原理一致,只是字符变成了“字母、数字、运算符、空白”等类别。

字符类别当前状态下一状态
字母初态标识符态
数字初态无符号数态
字母标识符态标识符态
非字母数字标识符态输出token并回到初态

用法是:把DFA的行列换成“状态×字符类别”,单元格里写下一个状态编号。识别循环每读一个字符,根据当前状态和字符类别查表,走到终态就输出token,然后回初态。这个练习能验证你对第三章掌握得是否扎实:漏掉空闭包的人写出的转移表会让标识符中间断掉;优先级理解错的人把a|bc类正规式翻译成DFA时会多出很多状态。如果PDF里的答案能顺利变成二维数组并跑通识别,说明你已经把纸面推导变成了实际能力。

5.3 从词法分析到符号表:答案之外的下一个台阶

第三章习题很少考符号表,但词法分析器真正运行时,识别token只是前半场。每个识别出的标识符都要去符号表里注册,记录名字、类型、行号等信息;关键字要区分是否保留字。编译原理符号表在实验里的典型流程是:识别循环收到一个标识符token后,先查符号表,存在则更新引用信息,不存在则插入新表项。这一步看似简单,但和DFA直接相关:DFA的终态类型决定了该查符号表还是直接输出token。比如识别到关键字状态,就不进符号表而是直接生成保留字token;识别到普通标识符终态,才执行查表逻辑。

所以学完第三章之后,下一步可以做的不是接着刷第四章习题,而是把这份PDF里某个DFA接上符号表逻辑。你甚至可以写一个极简的符号表类,用哈希表存标识符名和行号,让模拟器在每次进入终态时触发登记。做完这个串联,再用答案核对正规式本身对不对,才算把整章闭环。这也是为什么在实验课上,已经吃透第三章的人写词法分析器很快:他们不是在调规则优先级,而是在实现一张已经验证过的状态转移表。

6. 一个进阶用法:把习题答案转成自己的知识点错题表

第三遍刷这份答案时,别再按题号顺序过了,试试把PDF当成“错因对照表”来用。我会准备一张四列表格:章节知识点、题目关键词、当时错在哪、答案用了哪几步。每核对一道题就填一行,效率看起来比直接看答案慢,但复习时只需要看这张表,不用重新翻整份PDF。

知识点题目关键词我当时错在哪答案关键步骤
NFA转DFA给定NFA求DFA漏了ε-closure先闭包后字符转移
DFA化简求最小DFA终态与非终态合并第一组按终态二分
正规式等价证明a(b|c)*用了非法分配律化成同一NFA再比较

“修改后”这份PDF的另一个用法是标记差异:如果你的电脑里还留着旧版答案,或者在打印店见过初版,把改动过的题目在这张表里专门加一列“版本差异”。没有旧版也没有关系,直接标“推导跳步已补全”或“状态命名统一”,也能起到预警作用。因为你不知道修改过的内容里哪些是真正改错,哪些只是格式化整理,只有独立推演过的题才敢在考试时放心使用。

最后说一个我自己的教训:有一年考期前,我只背答案不手推,结果考场上一道NFA转DFA的化简题直接卡住,平时看答案明明每一步都“看得懂”。从那以后我给自己定了一条规矩——答案只用来对照,永远不替代推导。手推一遍哪怕和答案不一致,也比背一遍答案更有价值,因为不一致的地方恰恰是你和出题人理解的差异所在。这份PDF能不能发挥最大作用,取决于你愿不愿意在它旁边留下自己算错的痕迹。希望这篇笔记对你有用,也希望你刷完第三章后,能对词法分析这件事建立起自己的直觉。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询