在前一篇文章DFA算法的实现与最小化中介绍了DFA,这篇文章将介绍NFA。

1. NFA与DFA的区别

NFA与DFA的主要区别如下:

1) 对于一个特定的符号输入,DFA只会跳转到一个状态;而NFA则可能跳转到多个状态。

2) NFA中一个状态可以不经过任何符号就可以实现状态转换(即存在ε-转移)

上面两个区别就导致了NFA匹配符号串时经常要回溯,而DFA由于状态转移时不存在不确定性,效率比DFA

高很多,但另一方面NFA比DFA更灵活。NFA与DFA之间可以相互转换,后面将介绍NFA转换为DFA的算法。

2. NFA的构造

正如在前一篇文章DFA算法的实现与最小化中一样,NFA也继承了一个抽象类FA,如下所示:

public abstract class FA {protected List<FAState> acceptingStates;        //可接收状态(结束状态)protected List<FAState> states;                    //所有状态protected List<String> alphabet;                //符号字母表//状态转移矩阵(使用邻接链表表示)protected List<List<TransitMatElement>> stateTransitionMat; //....
}

下面是NFA类的定义

public class NFA extends FA {//开始状态protected List<FAState> startStates;//.......
}

之前定义DFA时,开始状态是用一个FAState类型的变量定义的,而这里,是用List<FAState>类型定义的。

这是因为DFA只能有一个开始状态,而NFA可以有多个开始状态。

构造NFA时,也是需要传入一个特定格式的文本文件的路径作为参数。

只不过由于NFA中可以存在ε-转移,需要在DFA的状态转移矩阵中添加一列,表示一个状态ε-转移的情况。

于是我就在DFA状态转移矩阵的基础上在最后一列的后面加上了一列,这反映在用于构造NFA的文本文件上

就是在DFA基础上增加了一列。由于之前在前一篇文章DFA算法的实现与最小化中已经详细地讲述过了,

这里就不再赘述了。
         下图给出了一个NFA的例子:


          这个例子与在介绍DFA时列出的例子等价,只不过这里状态3遇到a时有两种状态转换方式,

一种是转向状态4,另一种是转向自己。

下面举例说明另一种类型的NFA,这种NFA是由没有符号的弧(即ε-转移)引起的。

对于 这个 ε-转移,我们可以这样理解: 如果达到了状态4,可以不看当前的输入符号就转移到状态3。

所以,这是另外一种类型的非确定性。

3.NFA识别符号串

前面介绍过,DFA可以用来识别符号串,同样,使用NFA也可以。只不过由于NFA的不确定性,

NFA识别符号串的过程中可能会出现回溯。这样,我们就不得不将NFA识别符号串的过程中达到某一个

状态后可能跳转到的所有状态都保存起来。于是,我们就选择用栈来存放这些状态。

网上NFA识别符号串的例子很多,这里就不再举例子了,直接给出NFA识别符号串的核心算法。

/*** 使用自动机识别符号串(深度优先遍历)* @param words 待匹配符号串* @return 如果接受,则返回true,否则,返回false */public boolean recognize(String[] words) {//对于每一个开始状态,逐一尝试,看能否识别输入的符号串for(FAState state: this.startStates) {FAState currentState = state;int countOfWordsRecognized = 0;     // 用于存储识别的每一步中可能跳转到的所有状态Stack<FAState> agenda = new Stack<FAState>();while(countOfWordsRecognized <= words.length) {if(isAccepted(currentState, countOfWordsRecognized, words.length)) {return true;               } else if(wordsTerminatedButNotAccepted(currentState, words.length, countOfWordsRecognized)) {//当前开始状态下不能识别,尝试下一个开始状态break;} else {int indexOfAlpha = this.alphabet.indexOf(words[countOfWordsRecognized]);//当前符号串不在符号字母表中,识别失败if(indexOfAlpha < 0) { return false;} else {                       boolean isWordsRecgnized = generateNewStates(currentState, indexOfAlpha, agenda);if(isWordsRecgnized) {countOfWordsRecognized++;}}}          /*选当前开始状态时,当前步所有可能的状态都已经尝试,但未能匹配当前符号串。* 尝试下一个开始状态 */if(agenda.isEmpty()) {  break;} else {currentState = agenda.pop();   //进入下一个状态}} }return false;}

其中函数generateNewStates是用来产生遇到当前符号时可能跳转到的状态,并压入栈中的。其核心代码

如下:

/***  添加指定的状态遇到对应的符号串时所用可能进入的状态列表到状态栈agend* @param state* @param indexOfAlpha* @param agend 存放状态的栈* @return 当前单词是否被识别*/private boolean generateNewStates(FAState state, int indexOfAlpha, Stack<FAState> agend) {int indexOfState = this.states.indexOf(state);//获取下标为 indexOfState状态在状态转移矩阵中所对应的行List<TransitMatElement> transitMatEleRow = this.stateTransitionMat.get(indexOfState);List<FAState> states = new ArrayList<FAState>();boolean isWordRecognized = false;for(TransitMatElement transEle: transitMatEleRow) {//按照遇到的符号串的下标查找对应的要转移到的状态if(transEle.getAlphaIndex() == indexOfAlpha) {states.add(this.states.get(transEle.getStateIndex()));isWordRecognized = true;     //当前单词被识别} else if(transEle.getAlphaIndex() == -1) {  //ε-转移states.add(this.states.get(transEle.getStateIndex()));}}for(FAState curState : states) {if(!agend.contains(curState)) {  //当栈中不含有该状态时,才压入栈中agend.add(curState);}}return isWordRecognized;}

4. NFA转化为DFA

NFA转化为DFA的一种常用方法是子集法。我是参照《编译原理及实践教程》来实现的。这里,

引用该书中内容来加以阐述。

直接看这些概念应该会很无聊,下面,引用该书中的一个例子,来加以阐述。

相信看了这些概念和例子之后,你就能够实现NFA转化为DFA的算法了。如果还觉得有问题的话,可以

参考我实现的代码,可以到这里下载(注:这里的代码与之前的文章《DFA算法的实现与最小化》中的代码是

一样的,如果你已经下载了,就不用再下载了)

5.参考资料

1. 《编译原理及实践教程》, 黄贤英,王柯柯 编著

2. 《自然语言处理综述》, [美 ] Daniel Jurafsky 著

NFA构造及NFA转化为DFA相关推荐

  1. 将下图的nfa确定化为dfa_作业8 非确定的自动机NFA确定化为DFA

    1.设有 NFA M=( {0,1,2,3}, {a,b},f,0,{3} ),其中 f(0,a)={0,1}  f(0,b)={0}  f(1,b)={2}  f(2,b)={3} 画出状态转换矩阵 ...

  2. DFA和NFA的五元组定义,DFA和NFA的区别

    1.DFA的五元组定义:  DFA是一个确定有限自动机DFA, M是一个五元组  M = (S,∑,M,S0,F)  S: state非空有穷状态集合  ∑: 字母表,可输入符号  S0:start唯 ...

  3. 子集构造法NFA转换成DFA

    教材<编译原理>(龙书)第2版 关于这部分,教材在P94页有说明,但是我觉得不容易理解,下面通过两个例题来理解一下. 目录 例题一: 例题二: PS: 评论区: 第一次: 第二次: 第三次 ...

  4. java nfa dfa_DFA与NFA

    正则表达式引擎分成两类,一类称为DFA(确定性有穷自动机),另一类称为NFA(非确定性有穷自动机).两类引擎要顺利工作,都必须有一个正则式和一个文本串,一个捏在手里,一个吃下去.DFA捏着文本串去比较 ...

  5. 确定与不确定有限自动机DFA与NFA及子集法互相转换

    编译原理正规式NFA与DFA及子集法 1.基础术语与概念: 语法描述基本概念 字母表:一个有穷字符集记为Σ 字母表中每个元素称为字符 Σ上的字(字符串)是指Σ中的字符所构成的一个有穷序列 不包含任何字 ...

  6. DFA与NFA的比较

    正则表达式的规则很容易理解,但是正则表达式并不能直接用来解析字符串,我们还要引入一种适合转化为计算机程序的模型.今天我们引入的这种模型就叫做有穷自动机(finite automation,FA),有时 ...

  7. 形式语言与自动机基础

    基本概念 形式语法 形式语法是一个4元组 G=(N,Σ\SigmaΣ P, S), 其中: N 是非终结符的有限集合(有时也叫变量集或句法种类集); Σ\SigmaΣ是终结符的有限集合,Σ\Sigma ...

  8. 0/1背包问题——动态规划、回溯、分支限界法对比

    0/1背包问题--动态规划.回溯.分支限界法对比 2017.12.19 20:42:02 字数 3713 阅读 2820 目录 1.问题描述 1.1 问题描述 1.2 问题的数学表示(规划类问题,此种 ...

  9. 《编译原理》复习第1章~第5章

    前言 练习题来自超星<吉林大学编译原理>课程的章节测验和作业,全文仅为自己学习使用,如果有错,欢迎指正. 课时安排(课程重点) 2.4-2.7:正则式描述单词和确定有限自动机(DFA)部分 ...

最新文章

  1. python安装勾选-Python的安装
  2. OpenCV Python教程(3)(4)(5): 直方图的计算与显示 形态学处理 初级滤波内
  3. idea没有out文件夹_史上最详细没有之一的 Java JNI傻瓜级入门教程
  4. 什么是LambdaExpression,如何转换成Func或Action(2)
  5. RxJS之BehaviorSubject
  6. AG9 Service order创建好之后,要自动通过middleware传其他系统去 disable
  7. 查看linux服务器下接设备,linux下如何查看设备信息_网站服务器运行维护
  8. 逻辑回归的通俗解释 逻辑回归的定位
  9. 基于模型的系统工程MBSE软件工具(ModelCoder)
  10. 【JVM】GC Roots 根可达
  11. JMETER 线程组
  12. 区块链学习-以太坊学习简介
  13. 【翻译】RAID, Auto Volume, HBA, Simple Volume模式之间的区别
  14. 数组的属性、foreach遍历、交错数组与矩形数组的区别
  15. Xml遍历某一节点值
  16. cdr宏教程_如何自学coreldraw vba
  17. CRM管理系统源码,客户关系管理系统源码
  18. delphi android 升级,delphi android 自动升级
  19. 结构光激光器选择时应该注意的问题二:功率
  20. php安装zend loader,PHP5.3安装Zend Guard Loader图文教程

热门文章

  1. 电信业务分类目录_2019年7月国内增值电信业务许可情况分析报告:本期重点介绍内容分发网络业务...
  2. word日常排版——小技巧篇(非常实用)
  3. CSW博客《数字黄金》
  4. android启动windows7,WIN7 32位系统里安装和配置 Android Studio - 20130604
  5. 《分析服务从入门到精通读书笔记》第二章、多维OLAP优势篇(2)
  6. flac格式怎么转换成mp3?
  7. python怎么去掉视频字幕_利用Python实现字幕挂载(把字幕文件与视频合并)思路详解...
  8. MYSQL时间戳函数
  9. 集五福2019最全攻略
  10. 【VUE】draggable-实现组件拖拽实例