【问题标题】:What is the best way to build a DFA from an alphabet {a,b,c}?从字母 {a,b,c} 构建 DFA 的最佳方法是什么?
【发布时间】:2017-10-09 19:44:30
【问题描述】:

我正在尝试在字母 {a,b,c} 上构建一个 DFA,以接受具有三个连续相等字母的所有字符串的集合。

例如它可以接受:aaa, bbb, ccc, abbb, caaac, ccbbbcc, aaabbbc..

我尝试了不同的方法,但它变成了一个巨大的图表我想知道是否有更优雅的方法来做这个?

【问题讨论】:

    标签: computer-science automata finite-automata nfa


    【解决方案1】:

    首先,您的标题是 NFA,但您的问题正文是 DFA。我将回答这两种方式来说明为什么这很重要。

    首先考虑 NFA。我们只想接受具有三个连续相同类型符号的字符串。共有三个符号,因此可以通过三种方式发生这种情况(假设我们认识到字符串将在第一次出现三个连续符号后被接受)。我们可以看到任何东西,然后是三个相同的符号,然后再次看到任何东西。 NFA 很容易写下来:

         __
        /  \                  __
        |  / a,b,c           /  \
        V /                  |  / a,b,c
    --->q0--a->q1-a->q4-a-\  V /
        | \-b->q2-b->q5-b-->(q7)
        \---c->q3-c->q6-c-/
    

    我们的州执行以下操作:

    • q0:初始状态接受a、b和c的任何前缀。
    • q1, q4:只能由以 aa 作为子字符串的字符串访问的状态
    • q2, q5:只能由以 bb 作为子字符串的字符串访问的状态
    • q3、q6:只能由以 cc 作为子字符串的字符串访问的状态
    • q7:接受状态,只能被以 aaa、bbb 或 ccc 中的任何一个作为子字符串的字符串访问。

    在读取输入字符串的某些前缀后,NFA 不确定地分支以检查输入字符串是否包含 aaa、bbb 或 ccc,如果包含,则进入 q7 并接受可能剩余的后缀。

    要获得 DFA,实际上是最小 DFA,我建议按照 Myhill-Nerode 定理进行,按字典顺序检查字符串以查看它们是否与我们已经考虑过的字符串区分开来,因此将我们的 DFA 设计为一个状态为时间。

    1. 空字符串是可区分的。后面可以跟L中的任意字符串,得到L中的字符串。称其状态为[e]。
    2. 字符串 a 与空字符串是有区别的,因为它后面可以跟 aaL + L 得到一个 L 中的字符串。将其状态称为 [a]。
    3. 字符串 b 和 c 同样可以区分,并且具有状态 [b] 和 [c]。
    4. 字符串 [aa] 是可区分的,因为它后面可以跟 aL + L 以得到 L 中的字符串。将其状态称为 [aa]。
    5. 字符串 bb 和 cc 同样是可区分的,并且具有状态 [bb] 和 [cc]。
    6. ba 和 ca 与 a 无法区分;它们后跟与 a 相同的字符串以获取 L 中的字符串。
    7. ab/cb 和 ac/bc 同样分别与 b 和 c 无法区分。
    8. aaa 是可区分的,因为它后面可以跟任何东西,并且在语言中它仍然是一个字符串。
    9. bbb 和 ccc 与 aaa 没有区别。
    10. 长度为 3 的所有其他字符串与 a、b、c、aa、bb 或 cc 无法区分(检查此)
    11. 所有以 aaa 开头的长度为 4 的字符串都与较短的字符串没有区别(检查此)

    因为我们用完了可区分的字符串,我们知道我们已经列出了最小 DFA 的所有必要状态,我们可以写下答案:

                   +---a--->[a]<---a----+
                   | +-c--->[c]<---c-+  |
                   | |               |  |
         +----b--->[b]-------b------>[bb]---b----+
         |                                       |
         |         +---b--->[b]<---b----+        |   +--+
         |         | +-c--->[c]<---c-+  |        |   |  a,b,c
         |         | |               |  |        V   V  |
    --->[e]---a--->[a]-------a------>[aa]---a--->[aaa]--+
         |                                       ^
         |         +---a--->[a]<---a----+        |
         |         | +-b--->[b]<---b-+  |        |
         |         | |               |  |        |
         +----c--->[c]-------c------>[cc]---c----+
    

    (状态 [a]、[b] 和 [c] 分别重复两次,以使图表更漂亮。事实上,状态转换图不是平面的,渲染起来会很混乱,更不用说 ASCII 艺术了)。

    请注意,这与我们写下的简单 NFA 具有相同数量的状态 - 这恰好消除了不确定性。

    • 我们获得转换的方法是通过在符号 s 上从状态 [x] 到状态 [y] 来查看 xs 是否与 z 无法区分。
    • 我们获得初始状态的方式是它始终是 [e]。
    • 我们获得接受状态的方式是它是唯一一个字符串可以跟随为 e 以获取 L 中的字符串的方式。

    【讨论】:

    • 我不明白你是如何工作的 --a,c--> , ---b,c-->, ---a,b-->
    • @DictatorBoy 这些转换处理您开始看到 a、b 或 c 但随后看到其他内容并不得不“重新开始”的情况。正如我在括号中指出的那样,我复制了状态 [a]、[b] 和 [c] 以使图表更整洁,但重复的状态(没有过渡)代表真实的状态(有过渡)。
    • 是的,很难弄整齐,但这个点diagram 是最难追踪的。
    • @TomBlodget 我希望避免的那种鼠窝的一个很好的例子。也许在这种情况下,桌子会是更好的选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多