在这里创建 DFA 似乎更容易,因此我们可以从那里开始并从中派生正则表达式。
我们至少需要一个初始状态。此状态不能接受,因为空字符串不是以b 开头和结尾的。我们称之为q0。
如果我们在这种状态下看到a,我们正在查看一个不以b 开头的字符串,因此无论接下来发生什么我们都无法接受它。我们可以用一个新的、死亡的状态来表示这一点。我们称之为q1。
如果我们在q0 中看到b,我们需要一个新状态来表示我们正在顺利看到符合条件的字符串这一事实。实际上,字符串b 以b 开头和结尾,并且有偶数个a(零是偶数);所以这个状态一定是接受的。打电话给q2。
如果我们在q2 中看到a,那么我们有奇数个as,并且最后没有看到b,所以我们不能接受该字符串。但是,仍然可以通过看到奇数个 as 后跟至少一个 b 来接受来自此状态的字符串。调用状态来表示这个q3。
如果我们在q2 中看到b,我们的情况与以前相同(偶数个a,最后一次看到b,所以我们可以接受)。留在q2。
如果在q3 中,我们看到a,我们现在又有偶数个a,只需要一个b。将此新状态称为q4。如果我们看到b,我们仍然需要a,所以我们不妨留在q3。
如果在q4 中我们看到a,我们再次需要更多as 并且可以返回q3。另一方面,如果我们得到b,我们可以返回q2,因为该字符串是我们的语言。
DFA 如下所示:
q s q'
-- -- --
q0 a q1 q0: initial state
q0 b q2 q1: dead state, did not begin with b
q1 a q1 q2: accepting state, even #a and start/stop with b
q1 b q2 q3: start with b, odd #a
q2 a q3 q4: start with b, even #a, stop with a
q2 b q2
q3 a q4
q3 b q3
q4 a q3
q4 b q2
为了得到正则表达式,我们可以迭代地找到导致每个状态的正则表达式,然后将正则表达式的并集用于接受状态。在这种情况下,只有q2 接受,所以我们只需要该状态的正则表达式。我们迭代地进行,在每个阶段进行替换。
round 0
(q0): e
(q1): (q0)a + (q1)(a+b)
(q2): (q0)b + (q2)b + (q4)b
(q3): (q2)a + (q3)b + (q4)a
(q4): (q3)a
round 1
(q0): e
(q1): a + (q1)(a+b) = a(a+b)*
(q2): b + (q2)b + (q4)b = (b+(q4)b)b*
(q3): (q2)a + (q3)b + (q4)a = ((q2)+(q4))ab*
(q4): (q3)a
round 2
(q0): e
(q1): a(a+b)*
(q2): (b+(q3)ab)b*
(q3): ((q2)+(q3)a)ab* = (q2)ab* + (q3)aab* = (q2)ab*(aab*)*
(q4): (q3)a
round3:
(q0): e
(q1): a(a+b)*
(q2): (b+(q3)ab)b*
(q3): (b+(q3)ab)b*ab*(aab*)* = bb*ab*(aab*)*+(q3)abb*ab*(aab*)* = bb*ab*(aab*)*(abb*ab*(aab*)*)*
(q4): (q3)a
round4:
(q0): e
(q1): a(a+b)*
(q2): (b+bb*ab*(aab*)*(abb*ab*(aab*)*)*ab)b*
(q3): bb*ab*(aab*)*(abb*ab*(aab*)*)*
(q4): bb*ab*(aab*)*(abb*ab*(aab*)*)*a
因此,正则表达式是这样的:
r = (b+bb*ab*(aab*)*(abb*ab*(aab*)*)*ab)b*
= bb* + bb*ab*(aab*)*(abb*ab*(aab*)*)*abb*
-
bb* 部分对 b 的任何字符串在语言中都是字符串这一事实进行编码。
- 另一部分以
bb* 开头和结尾,它编码了任何字符串必须以b 开头和结尾的事实
- 最外层的
as 编码了这样一个事实:语言中的任何带有a 的字符串都必须有第一个和最后一个a
-
aab* 部分允许有连续的 a 对
-
abb*ab* 部分允许存在不连续的a 对
作为最后一点,上述替换表达式的规则如下:
A: r r is an expression
B: As s is an expression
=
A: r
B: rs
A: r + As r, s are expressions
=
A = rs*