文章目录

  • 回顾子集构造(NFA DFA)
  • 正则语言的闭包结果
  • 正则语言的 Union 依然是正则语言
  • 正则语言的 concatenate
  • 正则语言的 kleene~ star
  • 正则语言的其他闭包性质
  • 如何构造 DFA 的运算算法(构造 DFA 的交、并、补集)
  • 如何构造最小的 DFA(指包括最少状态数的 DFA)
  • 构造最小化 DFA 举例
  • 正则表达式
  • 正则表达式语法和语义
  • 正则表达式举例
  • 正则表达式和自动机(Regular Expression VS. Automata)
  • 构造单个起始状态的 NFA
  • 正则语言 NFA 举例(单个起始状态):(a ∪ b)^∗bc (a∪b)∗bc
  • 构造单个 accept 状态的 NFA
  • (兴趣读物:国内课本的方法)通过正则语言构造 NFA
  • 正则语言
  • 化简 “单个” 起始和 accept 状态的 NFA
  • 扩展
  • 正则表达式的定理
  • 正则语言的局限性
  • 通过泵引理(Pumping Lemma)来验证正则语言
  • 泵引理反证法实例


回顾子集构造(NFAjava 非 unicode 正则 非正则语言_bcDFA)

java 非 unicode 正则 非正则语言_bc_02

正则语言的闭包结果

正则语言的 Union 依然是正则语言

  • java 非 unicode 正则 非正则语言_正则语言_03 是两个正则语言,他们通过 java 非 unicode 正则 非正则语言_正则表达式_04 组成了一个 NFA ,他可以表示为 java 非 unicode 正则 非正则语言_java 非 unicode 正则_05;我们要在状态开始的时候使用 java 非 unicode 正则 非正则语言_正则表达式_04 来连接两个 DFA

正则语言的 concatenate java 非 unicode 正则 非正则语言_正则表达式_07

java 非 unicode 正则 非正则语言_正则表达式_08

  • 图中下半部分表示第一个语言的某两种状态通过 java 非 unicode 正则 非正则语言_正则表达式_04

正则语言的 java 非 unicode 正则 非正则语言_java 非 unicode 正则_10

java 非 unicode 正则 非正则语言_bc_11

正则语言的其他闭包性质

  • 两个正则语言的 intersection(交集)依然是正则语言
  • 正则语言的补集(complement)依然是正则语言 java 非 unicode 正则 非正则语言_正则表达式_12
  • 正则语言的差集(difference)依然是正则语言 java 非 unicode 正则 非正则语言_java 非 unicode 正则_13
  • 正则语言的取反(reversal)依然是正则语言

如何构造 DFA 的运算算法(构造 DFA 的交、并、补集)

java 非 unicode 正则 非正则语言_正则语言_14


java 非 unicode 正则 非正则语言_正则语言_15

如何构造最小的 DFA(指包括最少状态数的 DFA)

  • 因为我们无法保证通过某种算法可以得到最小的 DFA,如下图所示,我们不知道他是否为最简 DFA
  • 但是由于 DFA 拥有唯一的起始状态,并且转移函数是固定的,因此我们可以测试两个 DFA 的等价性从而找出最小的 DFA
  • 要构造最小的 DFA 要不断重复以下步骤
  • 翻转 NFA
  • 确定化结果
  • 再翻转
  • 再次确定化结果
  • 翻转 NFA 的方式也很简单:
  1. 那就是将所有的状态上的线调转方向
  2. 将接受(accept)状态节点和初始节点(start)互换

构造最小化 DFA 举例

  • 这是我们要最小化的 NFA, 我们在下面的步骤中通过它得到一个最小的 DFA
  • java 非 unicode 正则 非正则语言_bc_16

  • 第一步: 翻转(1节点和 2 节点的功能互换,原本 1 是初始节点,2是accept 节点,现在调转一下 1 变成了 accept 节点,2 变成了初始节点)
  • java 非 unicode 正则 非正则语言_bc_17

  • 第二步: 通过调转的 NFA 进行确定化得到当前状态下的 determinism 的结果
  • java 非 unicode 正则 非正则语言_java 非 unicode 正则_18

  • 因为最终状态中 java 非 unicode 正则 非正则语言_正则语言_19 包含原来的 java 非 unicode 正则 非正则语言_正则表达式_20 状态(即 accept 状态),因此,java 非 unicode 正则 非正则语言_正则语言_19
  • 第三步 再次调转已经得到的 NFA ;java 非 unicode 正则 非正则语言_正则语言_19 变成了起始状态;java 非 unicode 正则 非正则语言_正则表达式_23 变成了 accept 状态
  • java 非 unicode 正则 非正则语言_正则表达式_24

  • 第四步: 重复第二步的 determinism 得到最后的状态
  • java 非 unicode 正则 非正则语言_java 非 unicode 正则_25


  • java 非 unicode 正则 非正则语言_正则语言_26

正则表达式

  • 各种编程语言中几乎都涉及正则表达式
  • java 非 unicode 正则 非正则语言_正则表达式_27
  • java 非 unicode 正则 非正则语言_bc_28

正则表达式语法和语义

java 非 unicode 正则 非正则语言_java 非 unicode 正则_29


java 非 unicode 正则 非正则语言_正则语言_30

正则表达式举例

java 非 unicode 正则 非正则语言_java 非 unicode 正则_31

正则表达式和自动机(Regular Expression VS. Automata)

构造单个起始状态的 NFA

  • 正则表达式和有限状态的自动机是等价的,而且有限状态机的起始状态只能有一个
  • 在下面的例子中,我们假设每个 NFA 的起始状态只有一个,那么对于一个多起始状态的 NFA java 非 unicode 正则 非正则语言_正则语言_32 我们可以表示成若干个 java 非 unicode 正则 非正则语言_正则语言_33 的通过 java 非 unicode 正则 非正则语言_正则表达式_04 的并联
  • 这个式子java 非 unicode 正则 非正则语言_正则语言_35 中,java 非 unicode 正则 非正则语言_正则语言_36 代表的就是原本的 多个起始状态 java 非 unicode 正则 非正则语言_正则表达式_37 统一用 java 非 unicode 正则 非正则语言_正则语言_36 + java 非 unicode 正则 非正则语言_正则表达式_04 代替;而其他不是起始状态开始的节点则遵循原本的 java 非 unicode 正则 非正则语言_正则表达式_40 转换状态。
正则语言 java 非 unicode 正则 非正则语言_bc NFA 举例(单个起始状态):java 非 unicode 正则 非正则语言_bc_42
  • 国外的书籍和课件 是按照这种方式进行构造和转换的

构造单个 accept 状态的 NFA

  • 可以看到下图的 java 非 unicode 正则 非正则语言_正则语言_32
  • 汇总起始状态和将初始状态分开都同样使用 java 非 unicode 正则 非正则语言_正则表达式_04
  • 例如下图的例子:
  • 图中的 java 非 unicode 正则 非正则语言_正则语言_35 代表的就是将状态转换函数分成了两类:
  • 如果是原来 accept 状态,那么就添加一个新的状态 java 非 unicode 正则 非正则语言_java 非 unicode 正则_46 并且把原来所有的 accept 状态都通过 java 非 unicode 正则 非正则语言_正则表达式_47
  • 原来的其他状态则不需要进行调整,维持原本的样子
  • 所以我们看到下图中的三个原本的 accept 状态都通过 java 非 unicode 正则 非正则语言_正则表达式_04 连到了新的 “唯一的 accpet” 状态 java 非 unicode 正则 非正则语言_java 非 unicode 正则_49

(兴趣读物:国内课本的方法)通过正则语言构造 NFA

  • 当我们获得一个正则语言,我们如果要构造 NFA(单起始状态的),我们只需要不断重复下面 三个步骤 即可:(国内书籍版本)
  • 将 concatenate 操作分成两个串联的部分
  • 将 union (|)操作分成两个并联的部分
  • 将闭包运算 * 分成第三种情况
正则语言 java 非 unicode 正则 非正则语言_bc

java 非 unicode 正则 非正则语言_bc_51

化简 “单个” 起始和 accept 状态的 NFA

  • 上文已经分别介绍了如何构造单个起始状态的 NFA 和 单个 accept 状态的 NFA
  • 现在对于中间的状态进行重复地替换(使用正则表达式)以简化 NFA;方法就是把线上的 字符 用正则表达式来替换;并不断重复这个过程
  • 如下图所示,我们的 NFA 现在已经是 单个起始状态和单个 accpet 状态;弧线上表示的 java 非 unicode 正则 非正则语言_正则表达式_52 都是 正则表达式,假设我们通过化简可以得到下面的两个 NFA 的表示 :
  • java 非 unicode 正则 非正则语言_正则表达式_53
  • java 非 unicode 正则 非正则语言_java 非 unicode 正则_54

    通过下面例子来进行演示:假设化简的是下面的例子
  • 首先先把上面的两个 accept 状态的图转换成一个 accept 状态,根据上面的知识
  • 通过正则表达式来替换线上的字符从而实现状态的化简:
  • 再次通过正则表达式来合并中间的步骤
  • 最终把中间状态逐渐换成正则表达式;得到了最简的 NFA
  • 而上述的式子就相当于我们最开始引入的 java 非 unicode 正则 非正则语言_bc_55
  • 因此我们容易得到以下替换:
扩展
  • 下图中表示的:一个进,一个出,一个循环的这种状态可以被固定的写成 java 非 unicode 正则 非正则语言_java 非 unicode 正则_56

java 非 unicode 正则 非正则语言_java 非 unicode 正则_57

  • 如果有 java 非 unicode 正则 非正则语言_正则表达式_58 个进入的弧线,java 非 unicode 正则 非正则语言_正则表达式_59 个出去的弧线,那么这些弧线可以被 java 非 unicode 正则 非正则语言_java 非 unicode 正则_60

正则表达式的定理

java 非 unicode 正则 非正则语言_bc_61


java 非 unicode 正则 非正则语言_bc_62

正则语言的局限性

java 非 unicode 正则 非正则语言_bc_63

  • 对于上面的语言我们无法使用 DFA 来识别,即:他不是一个正则语言。

通过泵引理(Pumping Lemma)来验证正则语言

java 非 unicode 正则 非正则语言_正则表达式_64


java 非 unicode 正则 非正则语言_正则表达式_65


java 非 unicode 正则 非正则语言_正则表达式_66


java 非 unicode 正则 非正则语言_bc_67

  • pumping lemma 只能证明一个语言不是正则语言,不能证明一个语言是正则语言;即:满足 pumping lemma 不一定是正则语言,但是不满足 pumping lemma 一定不是正则语言

泵引理反证法实例

java 非 unicode 正则 非正则语言_正则表达式_68


java 非 unicode 正则 非正则语言_java 非 unicode 正则_69


java 非 unicode 正则 非正则语言_正则表达式_70


java 非 unicode 正则 非正则语言_java 非 unicode 正则_71