CS143:Finite Automata(regular expression->NFA->DFA)

在上个博客中,我们已经知道了,词法分析中的词法规范就是用正则表达式来实现的,我们用正则表达式来表述了编程语言中各种不同的token class。

而有限自动机也是一种正则表达式的应用实现,它和正则表达式密切相关,它能够表述和正则表达式一样的规范

自动机中一些常见的符号

\sum:一组能够读取的合法的输入字符集

S:一组有限的状态

n:一个开始状态

F:一组可接受状态

->:一组转化公式,在指定状态读取到指定输入值后可以转化为另种状态

If end of input and in accepting state => accept :自动机可以不断读取输入的字符串,并基于输入不断从当前状态转变为新的状态,当字符串被读取结束后,要求状态必须处于aceepting state,这样才能表明这个字符串是合法的,被接受的。

Otherwise => reject : 如果输入的字符串在读取结束后,最后的状态S \notin F不属于可接受的状态,或者说在读取的中途发现没有可用的transition进行状态转化,那么都会被拒绝,表示则个字符串是非法的。

自动机常见图形表示方法

自动机语言

Language of a FA \equiv set of accepted strings

有一个比较常见的词,自动机语言,其实自动机语言就是指那些能够被该自动机接受的字符串的集合。

NFA和DFA

\varepsilon-move(空跳)

这是这种特殊的状态转化,表示从状态A可以什么都不接受直接转化为状态B

DFA

One transition per input per state

No \varepsilon-move

DFA中要求一个状态接受一个输入,只可能转化为另一个确定的状态。每个输入在DFA中有一条已经确定的转换路径,当这条路径的转换结果决定了这个输入是accepted还是reject。

因此DFA中就不允许有\varepsilon-move转换存在

NFA

Can have multiple transitions for one input in a given state

Can have \varepsilon-move

NFA允许一个状态接受一个输入时,可能转化为多种不同的状态。每个输入在NFA就有多种转换路径可以选择,部分可能是accepted,部分是reject,只要有一条路径是accepted,那这个输入就是accepted的。

因此NFA中就允许有\varepsilon-move转换存在

NFA和DFA均能表示正则表达式

因此DFA和NFA的最大差别就在于没有空跳的存在

DFA执行的效率更高,因为无需做选择,NFA有多种可能性

NFA的优势在于通常情况会更小,一般规模是成指数级别的缩小

Regular Expressions to NFAs

一般来说,正则表达式中可以有以下常见的NFA转化方法

AB,两个正则表达式的级联

A+B,两个正则表达式的并集

A^*,A的Iteration

我们可以利用上述的3个公式来将一个复杂的正则表达式来分步骤绘画出NFA

NFA to DFA

\varepsilon-closure

某个状态S的eplison闭包,举例说明:closure(B) = {B 、C、D},我们看到闭包中的状态集合就是这个状态可以通过空跳到达的状态的集合

下面我们来思考一个问题

NFA读取一个字符串进行处理的时候,处理过程中由于每次状态都有多种选择,因此可能处于多种状态,那么这个多种状态的可能性有几种?

假设一个NFA的状态数量为N,那么这个NFA在执行过程中就有2^n-1种不同非空状态的组合,我们可以看到NFA执行过程中状态的组合数量是有限的, 虽然这个数字可能会很大。

finit set of possible configurations:状态组合的数量有限的特点给NFA->DFA的转化提供了灵感

上图是给出NFA和DFA转化关系

NFA中状态集合S,开始状态s,接受状态集合F,以及一系列转化公式和空跳转换

那么如何将这个NFA转换为DFA呢?

我们将NFA中每个可能的状态组合都作为一种新的状态来看待

DFA中的状态集合S'=S的子集

开始状态s'=s的epsilon的闭包

接受状态集合:\{x|x\cap F \ne \empty, x\in S'\}

转换公式相比NFA中将空跳删除,并将其余的转换公式中的目标状态改为epsilon的闭包

如何表示DFA

DFA可以使用一个二维数组来进行表示

一维是状态,另一维是字符

对于一个转换公式S_i\rightarrow^aS_j,在这个二维数组中表示方式为T[i,a] = j

// 对于DFA而言,代码实现非常简单紧凑
int i = 0;
int state = 0;
while (input[i])
{
    s = T[s][input[i++]];
}

对于一个有N个状态的NFA而言,转换成为一个DFA,可能DFA的状态数量会非常多,我们在上面已经计算过DFA的状态可能有2^N - 1个,这就会导致DFA的这个二维数组变得非常大。

如何解决二维数组过大的问题?如何进行优化?

这个二维数组可能存在很多重复行,避免重复数据的存储,我们可以改用指针的形式来实现更加紧凑的存储。

NFA->DFA的转换并非必要

NFA的状态数量很少,但是转换过程中由于存在多种状态的可能性,我们需要实时保存当前可能所处的状态,然后对遍历这些可能的状态进行状态的转换。

NFA的特点就在于,内存开销小,但是计算开销会大大增加,速度慢

DFA的状态数量很多,但是转换过程每次只可能从一个确定状态转化到另个确定的状态

DFA的特点在于,内存开销大,但是计算开销小,性能很高

因此,NFA和DFA之间的选择其实就是计算开销和内存开销之间的trade off,并非任何时候都是DFA适用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值