目标:得到状态数最少、但识别同一语言的等价 DFA。做法是把"行为相同"的状态合并[1]。
① 先删掉不可达状态。
② 初始分两组:终态组 F 与 非终态组 (S−F)。
③ 细化:检查同一组里的状态,对某输入符 a 转到的目标若落在不同的现有组,就把这一组按"目标所属组"裂开。
④ 重复③直到没有组能再分。每个最终组并成一个状态。
| 状态 | 0 | 1 |
|---|---|---|
| A(初) | C | B |
| B(终) | C | B |
| C | C | D |
| D(终) | C | D |
A: 0→C(在G1), 1→B(在G2) C: 0→C(在G1), 1→D(在G2) 两者“0 进 G1、1 进 G2”签名相同 → 不分裂。③ 查 G2={B,D}:
B: 0→C(G1), 1→B(G2) D: 0→C(G1), 1→D(G2) 签名相同 → 不分裂。④ 没有组能再分,最终两组:{A,C}→记作 P,{B,D}→记作 Q。最小化 DFA(2 个状态):
0 1 P(初) → P → Q Q(终) → P → Q结论:原 4 状态化简为 2 状态(它识别的正是"以 1 结尾的 0/1 串")。∎
功能:读入字符流,输出单词 token。token 通常是二元组 (种别码, 属性值)。它还顺带:滤掉空白与注释、查/填符号表、报词法错误[2]。
用状态转换图实现:节点是状态、边标字符;读一个字符走一条边,到达终态 = 识别出一个单词。
if 既像标识符,查表才知是关键字)。 字母 字母/数字
(0) ───────────→ (1) ⟲ ───────────┐
start 终态 ←────────────┘
│ 其它字符(退回1个)
↓
识别出一个标识符
驱动伪代码:
state0: c = getchar();
if c 是字母 then goto state1 else error;
state1: c = getchar();
if c 是字母或数字 then goto state1
else { retract(c); // 超前搜索:退回一个字符
返回 (标识符, 词文); }