首先,找到最外层的操作。在您的示例中,它是+。当您有+ 时,这意味着您可以接受左边的东西或右边的东西。我们可以使用空(lambda 或 epsilon)转换在 NFA 中对此进行编码,如下所示:
Q s Q'
q0 - M1
q0 - M2
我们以q0 为起点,我们使用M1 和M2 来表示机器,它们分别接受正则表达式的LHS 和RHS 生成的字符串。当我们在 lambda/epsilon 上说 q0 转换为 M1 和 M2 时 - 空转换 - 我们的意思是我们不确定地选择走哪条路径。转换将从q0 到M1 和M2 的初始状态,无论这些状态恰好是什么。
现在我们在每个 LHS 和 RHS 上递归地重复该过程。我们可以从 RHS 开始,因为它更简单。这里最外层的操作是连接(符号a 和b)。串联很容易表示:
Q s Q'
q2 - M3
M3 - M4
这里,q2 是 M2 之前的初始状态,M3 和 M4 代表目前尚未确定的机器,它们分别接受 @987654340 的串联的 LHS 和 RHS @ 和 b。当我们说q2 转换到M3 时,我们的意思是它转换到M3 的初始状态;当我们说M3 转换为M4 时,我们的意思是M3 的所有接受状态转换为M4 的初始状态。
递归进行,我们现在需要a 和b 的机器。它们都具有以下形式:
Q s Q'
q x q'
q 是初始状态,x 是符号,q' 是接受状态。所以我们得到:
Q s Q'
q3 b q4 (q3 initial, q4 accepting)
和
Q s Q'
q5 a q6 (q5 initial, q6 accepting)
我们已经到达了这个递归分支的底部,可以后退一步,根据我们定义的具体机器在转换表中生成具体条目。我们有这个:
Q s Q'
q2 - M3
M3 - M4
现在我们知道M3 和M4 的样子,所以我们可以替换:
Q s Q'
q2 - q3
q3 b q4
q4 - q5
q5 a q6 (q2 initial, q6 accepting)
现在我们准备好从+ 操作中执行 LHS。最外层的操作是*。我们在 NFA 中处理这些的方式如下:
Q s Q'
q7 - M5
M5 - M5
我们现在考虑下一个操作,连接。我们已经对此进行了介绍,并且我们知道我们得到了这个:
Q s Q'
q8 - M6
M6 - M7
现在我们需要a 和b。同样,我们知道这些看起来像:
Q s Q'
q9 a q10
和
Q s Q'
q11 b q12
我们将它们重新组合在一起:
Q s Q'
q8 - q9
q9 a q10
q10 - q11
q11 b q12 (q8 initial, q12 accepting)
然后我们做 Kleene 明星:
Q s Q'
q7 - q8
q8 - q9
q9 a q10
q10 - q11
q11 b q12
q12 - q8 (q8 initial, q8 and q12 accepting)
最后,我们将所有规则合并到一个大的转换表中:
Q s Q'
q0 - q2
q0 - q7
q2 - q3
q3 b q4
q4 - q5
q5 a q6
q7 - q8
q8 - q9
q9 a q10
q10 - q11
q11 b q12
q12 - q8 (q0 initial, q6, q8 and q12 accepting)
因此,您可以递归地为任何正则表达式构造 NFA。生成的 NFA 在一般情况下会有一些不必要的状态,但 NFA 优化是一个微妙的话题。您始终可以使用此(或任何)NFA,使用已知算法转换为 DFA,然后使用已知算法最小化。那么你就有了一个可证明的最小 DFA,尽管它可能比这个填充的 NFA 大得多!