dfa算法 java_DFA算法的实现与最小化
有限自动机分为确定有限自动机(DFA)和不确定有限自动机(NFA),这里介绍DFA,即确定有限自动机。
1. DFA的形式定义
从形式上说,一个有限状态自动机可以用下面的5个参数来定义:
Q: 状态q0, q1, ... , qN的有限集合
Σ: 有限的输入符号字母表
q0: 初始状态
F: 终极状态的集合, F∈Q
δ(q, i): 状态之间的转移函数或转移矩阵。给定一个状态q∈Q和一个输入符号i∈Σ, δ(q, i)返回一个新的状态q'∈Q。
2.DFA的java实现
这里定义了一个抽象父类FA,DFA和NFA继承了FA类。
publicabstractclassFA {
protectedList acceptingStates;//可接收状态(结束状态)
protectedList states;//所有状态
protectedList alphabet;//符号字母表
//状态转移矩阵(使用邻接链表表示)
protectedList> stateTransitionMat;
//....
}
publicclassDFAextendsFA {
protectedFAState startState;//开始状态
}以上就是自己实现的DFA对应的类实例变量的定义。其中 FAState 类表示一个状态节点,包含一个实例属性id,
用于唯一区分一个FAState。
构造DFA对象时,需要传入一个特定格式的文本文件的路径。
文件的格式,举例如下:
5 3
1 2 3 4 5
b a !
1
5
1 -1 -1
-1 2 -1
-1 3 -1
-1 3 4
-1 -1 -1
其中,第一行的两个数字分别表示自动机状态的数目和符号字母表中符号的数目
第二行的数字为每一个状态的id
第三行为符号串(符号串间以空格隔开)
第四行的数字为开始状态的id
第五行的数字为结束状态的id
最后几行为状态转换矩阵(其中数字表示跳转到的状态在状态列表中的下标,-1表示一个状态不能遇到相应的符号)
上面的例子表示的DFA对象如下图所示:
3. 使用DFA识别符号串
DFA构造好了之后就可以用来识别符号串。这里引用了NFA/DFA算法 - 于公摊的杂货铺中的一个例子:
如何通过设计状态及其转移方法来实现一个分析器呢?当然,如果一个字符串仅仅包含a或者b的话,那么分析器的状态只有四种:
“奇数a奇数b”、“奇数a偶数 b”、“偶数a奇数b”、“偶数a偶数 b”。我们把这些状态依次命名为aa、aB、Ab、AB。大写代表偶数,
小写代表奇数。当工作还没开始的时候,分析器已经读入的字符串是空串,那么理所当然的起始状态应当是AB。当分析器读完所有
字符的时候,我们期望读入的字符串的a和b的数量都是偶数,那么结束的状态也应该是AB。于是我们给出这样的一个状态图:
图3.1 检查一个字符串是否由偶数个a和偶数个b组成的状态图
在这个状态图里,有一个短小的箭头指向了AB,代表AB这个状态是初始状态。AB状态有粗的边缘,代表AB这个状态
是结束的可接受状态。 一个状态图的结束状态可以是一个或者多个。在这个例子里,起始状态和结束状态刚好是同一个状态。
标有字符”a”的箭头从AB指向aB, 代表如果分析器处于状态AB并且读入的字符是a的话,就转移到状态aB上。我们把这个状态图
应用在两个字符串上,分别是”abaabbba”和”aababbaba”。
其中,第一个字符串是可以接受的,第二个字符串是不可接受的(因为有5个a和4个b)。
分析第一个字符串的时候,状态机所经过的状态是:
AB[a]aB[b]ab[a]Ab[a]ab[b]aB[b]ab[b]aB[a]AB
分析第二个字符串的时候,状态机所经过的状态是:
AB[a]aB[a]AB[b]Ab[a]ab[b]aB[b]ab[a]Ab[b]AB[a]aB
第一个字符串”abaabbba”让状态机在状态AB上停了下来,于是这个字符串是可以接受的。第二个字符串”aababbaba”让状态机
在状态aB上停了下来 ,于是这个字符串是不可以接受的。
DFA识别符号串的核心算法实现如下:
/**
* 使用自动机识别符号串
* @param words 待匹配符号串
* @return 如果接受,则返回true,否则,返回false
*/
publicbooleanrecognize(String[] words) {
FAState currentState = this.startState;
intcountOfWordsRecognized =0;
while(countOfWordsRecognized <= words.length) {
if(isAccepted(currentState, countOfWordsRecognized, words.length)) {//接收
returntrue;
} elseif(wordsTerminatedButNotAccepted(currentState, words.length,
countOfWordsRecognized)) {
returnfalse;
}
// 当前待识别的单词在alphabet中的下标
intindexOfAlpha = alphabet.indexOf(words[countOfWordsRecognized]);
//查找状态转移矩阵,获取将要跳转到的状态的下标
inttransition =
getIndexOfStateToSwitch(states.indexOf(currentState), indexOfAlpha);
if(transition == -1) {//不能匹配当前符号串,拒绝
returnfalse;
} else{
currentState = this.states.get(transition);//进行下一个符号串的接收
countOfWordsRecognized++;
}
}
returnfalse;
}
4.DFA最小化
4.1 最小状态DFA的含义
1.没有多余状态(死状态)
2. 没有两个状态是互相等价(不可区别)
4.2 两个状态s和t等价的条件
兼容性(一致性)条件——同是终态或同是非终态
传播性(蔓延性)条件——从s出发读入某个a和从t出发经过某个a并且经过某个b到达的状态等价。
4.3 举例说明
下面就以如下图所示的DFA对象为例说明最小化DFA的操作
DFA的最小化
1. 将状态列表S中的状态分为两个子集:终结状态s1(也叫可接受状态)和非终结状态s2
对于上图中的DFA, s1 = {4, 5, 6, 7}, s2 = {1, 2, 3}
2. 查看s2 = {1, 2, 3}是否可以继续划分
状态2遇到a转向s1中的状态4,不在s2中,于是将s2继续划分,分为{1, 3}和{2}这两个集合。
继续查看{1, 3}是否可以继续划分。由于3遇到b转向s1中的状态5,于是将{1, 3}继续划分,分为{1}和{3}这两个集合。
3. 查看 s1= {4, 5, 6, 7}是否可以继续划分
因为4, 5, 6, 7经过a和b跳转到的状态都在s1中,不可再分。因而这四个节点可以用一个节点来表示。
需要注意的是,当最终所以的子集不可再分时,如果一个子集内的状态不止一个,则由于同一子集内的状态等价,
同一子集内的节点之间的状态跳转可以不用考虑。但是如果这个子集内的某一状态遇到一个符号转向本身时,
这种向自身的转移要体现在新的替换的状态上。例如4,5,6,7最终属于同一子集,最终用节点4来替换这4个节点时,
需要在状态转移矩阵中加上遇到a跳转到节点4,与遇到b跳转到节点4的情况。
最终得到的最小化DFA如下图所示:
4.4 具体实现
DFA最小化的核心算法如下:
/**
* 最小化当前DFA对象
*/
public void simplify() {
//用于存放最小化的过程中产生的状态划分
List>stateLists=newArrayList>();
// phrase 1: 将化简前的DFA的状态分为非可接受状态和可接受状态两部分
ListnonTerminalStates=newArrayList();
ListcopyOfOriginalState=deepCloneOfFAStateList(this.states);
for(FAState state : copyOfOriginalState) {
if(!this.acceptingStates.contains(state)) {
nonTerminalStates.add(state);
}
}
ListterminalStates=deepCloneOfFAStateList(this.acceptingStates);
stateLists.add(nonTerminalStates);
stateLists.add(terminalStates);
// phrase 2: 看nonTerminalStates能否再分,如果可以,则进行划分
splitStateListIfCould(stateLists, nonTerminalStates);
// phrase 3: 看terminalStates能否再分,如果可以,则进行划分
int leftMostEndStateIndex=splitStateListIfCould(stateLists, terminalStates);
// phrase 4: 根据存储状态列表的列表的每一个元素作为一个状态,构造最小化DFA
rebuildDFAWithSimplifiedStateList(stateLists, leftMostEndStateIndex);
}
dfa算法 java_DFA算法的实现与最小化相关推荐
- java开发抢红包算法,抢红包算法的实现-java
二倍均值法 原理 原理 剩余红包金额M,剩余人数N,那么:每次抢到金额=随机(0,M/N*2) 保证了每次随机金额的平均值是公平的 假设10人,红包金额100元 第一人:100/10*2=20,随机范 ...
- 编译原理学习笔记(十五)~最小化DFA
概念 最小化:优化DFA,使其状态数最少. 那么什么时候状态数是最少的呢?这里我们需要介绍两个新的名词:可区分和不可区分. 官方定义: 可区分:对于任何两个状态t和s,若从一状态出发接 ...
- 编译原理——正规式转DFA算法概述
一.概念概述 给定一个单词,判断该单词是否满足我们给定的单词描述规则,需要用到编译原理中词法分析的相关知识,其中涉及到的两个很重要的概念就是正规式(Regular Expression)和有穷自动机( ...
- dfa算法 java_Java实现DFA算法对敏感词、广告词过滤功能示例
一.前言 开发中经常要处理用户一些文字的提交,所以涉及到了敏感词过滤的功能,参考资料中DFA有穷状态机算法的实现,创建有向图.完成了对敏感词.广告词的过滤,而且效率较好,所以分享一下. 具体实现: 1 ...
- 倒排索引:ES倒排索引底层原理及FST算法的实现过程
文章目录 引言 1.倒排索引核心原理 2.倒排索引的存储结构 2.1 倒排表(Posting List) 2.2 词项字典(Term Dictionary) 2.3 词项索引(Term Index) ...
- python实现dfa过滤算法_Hopcroft算法DFA最小化Python实现
DFA最小化原理 所谓自动机的化简问题即是对任何一个确定有限自动机DFA M,构造另一个确定有限自动机DFA M',有L(M)=L(M'),并且M'的状态个数不多于M的状态个数,而且可以肯定地说,能够 ...
- 最小化局部边际的合并聚类算法(中篇)
作者:钱烽 三.合并聚类算法 基于定义2所提出的相似度定义,我们在图2中给出最小化局部边际的合并聚类算法详细执行过程.首先,针对数据集中可能存在的噪声数据,我们对所有样本点进行孤立点检测.然后,作为A ...
- 游戏中常用的寻路算法的分享(3):A*算法的实现
概述 剥除代码,A* 算法非常简单.算法维护两个集合:OPEN 集和 CLOSED 集.OPEN 集包含待检测节点.初始状态,OPEN集仅包含一个元素:开始位置.CLOSED集包含已检测节点.初始状态 ...
- 关于寻路算法的一些思考(3):A*算法的实现
概述 剥除代码,A* 算法非常简单.算法维护两个集合:OPEN 集和 CLOSED 集.OPEN 集包含待检测节点.初始状态,OPEN集仅包含一个元素:开始位置.CLOSED集包含已检测节点.初始状态 ...
最新文章
- 生日游戏html,小朋友过生日室内游戏
- UNIX下C语言的图形编程-curses.h函数库
- 1. 机器学习-特征工程
- 架构漫谈 - 数据治理核心思路及解决方案探讨
- 数据库技术mysql能干什么_MySQL外键有什么作用
- SpringMVC 快速入门
- 销售管理软件系统的两大优势是什么?
- dnf时装补丁教程_【时装补丁制作】消灭伸手党!最详细的图文教程~
- 互联网公司裁员还有秘密?我知道了!
- 年薪30万IT精英 挥别都市回乡种田务农
- xposed android debug,Android 手机开启全局调试xposed插件
- qcustomplot 峰值_QT 本程序可读取Yokogawa 光谱仪多个数据文件(CSV格式)并寻峰 - 下载 - 搜珍网...
- 批发进销存软件哪个好用?求测评
- kali下经典的ddos攻击软件_Kali-DDoS工具集合
- 白痴学日语系列之Last Term附语法一
- 网易我的世界服务器加载无响应,我的世界网易版开始游戏没反应
- 荣耀YOYO建议新增快递取件服务
- win8的prolific USB-to-serial comm port驱动安装问题解决
- 智能AR场景模拟书法临摹系统
- Android面试问答题