正则表达式的DFA算法.docx

上传人:b****5 文档编号:3237346 上传时间:2022-11-20 格式:DOCX 页数:25 大小:99.87KB
下载 相关 举报
正则表达式的DFA算法.docx_第1页
第1页 / 共25页
正则表达式的DFA算法.docx_第2页
第2页 / 共25页
正则表达式的DFA算法.docx_第3页
第3页 / 共25页
正则表达式的DFA算法.docx_第4页
第4页 / 共25页
正则表达式的DFA算法.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

正则表达式的DFA算法.docx

《正则表达式的DFA算法.docx》由会员分享,可在线阅读,更多相关《正则表达式的DFA算法.docx(25页珍藏版)》请在冰豆网上搜索。

正则表达式的DFA算法.docx

正则表达式的DFA算法

正则表达式

1正则表达式的定义

正则表达式(RegularExpression)是一种强大的,便捷的,高效的文本处理工具,它可以表示比单字符、字符串集合等更加复杂的搜索模式。

下面首先给出正则表达式和它所表达语言的形式化定义。

一个正则表达式RE是符号集合工{£;|,*,(,)}上的一个字符串,它可以递归定义如下:

空字符£是正则表达式。

任意字符a€工是正则表达式。

如果REi和RE2都是正则表达式,则(REi),(RE1RE2),(RE1|RE2)和(REi*)亦是正则表达式。

通常(REiRE2)可以简写为RE1RE2。

符号“;”“*“称为操作符,可以通过为每个操作符赋予优先级来消除更多的括号。

为了方便起见,这里使用了额外的后缀操作符“+”,

它的含义是RE+=RERE*。

其他所使用的操作符如”?

,”字符组,”•等实际上都可以用上面的方式来表达。

下面定义正则表达式所表达的语言。

正则表达式RE所表达的语言是工上的一个字符串集合。

根据RE的结构,可以将它递归的定义如下:

如果RE是£,则L(RE)={门即空串。

如果RE是a€X,则L(RE)={a,即包含一个字符的单串。

如果RE是(REi)这种形式,则L(RE)=L(REi)。

如果RE是(REiRE2)这种形式,则L(RE)=L(REi)L(RE2),其中WiW2可以看成是字符串集W的集合,其中,W=WiW2并且Wi€Wi,W2^W2。

操作符表示字符串的连接。

如果RE是(REi|RE2)这种形式,贝UL(RE)=L(REi)UL(RE2),是这两种语言的并集,称为并操作符。

如果RE是(REi*)这种形式,则L(RE)=L(RE)*=Ui>oL(RE)i,其中Lo={&并且Li=LLi-i,它表示字符串集合是由0个或者多个REi表达的字符串连接而成。

“*称“为星操作符。

正则表达式RE的规模是指它所包含的属于字母表工的字符的个数,在算法复杂性分析

中,它是一个重要的度量。

在文本T中搜索正则表达式RE的问题就是找到文本中所有属于语言L(RE)的字串。

索的方法是首先将正则表达式解析成一颗表达式树,然后将表达式树转换成非确定性有限自动机(NFA)。

直接使用NFA进行搜索是可行的,然而NFA算法处理速度通常比较慢,一

般的,搜索过程最坏情况时间复杂度是0(mn),但是所需存储空间并不多。

另外一种策略是

将NFA转变成确定性有限自动机(DFA),它的搜索时间是0(n),但是构造这样的一个自动

机所需的最坏情况时间和空间复杂度都是0(2m)。

2构造解析树

通常来说,解析树并不是唯一的。

在解析树中,每个叶节点都是使用SU{门中的一个字符来标识的,而每个中间节点则使用操作符集合{|,-,*}中的一个进行标识。

一种可能的解析树使用二叉树来表示,二叉树的父节点是一个操作符,两个子节点表示

这个操作符作用的两个子表达式。

如正则表达式(AT|GA)((AG|AAA)*)的解析树可以表示如

下:

A

A。

那么上面那个正则表达式的

存储序列如下:

AT-GA-|AG-AA-A-|*・。

函数re2post就是将输入的正则表达式字符串转换成解析树的后序遍历序列。

解析过程中有两个重要的变量,natom和nalt,natom表示解析到这个字符为止,已经有多少个原子结构,而nalt表示解析到这个字符为止,已经有多少个分支结构。

正则表达式中的括号表示

一个子表达式,这个子表达式对于括号外面的表达式来说是一个原子结构,它内部的natom

和nalt的值和外部的表达式的这些值没有关系。

为了正确的处理这种括号及其嵌套,程序中

使用堆栈来辅助解析,每当碰见"(“,将当前的natom和nalt压入栈中,新的natom和nalt从零开始;而解析到”)“时,则根据当前的natom和nalt值进行后续处理,然后从栈中弹出上一层的natom和nalt。

具体的处理算法如下:

Parse(p=p1p2…pm,last)

v=0;

Whileplast丰$Do

Ifplast€工Then

If(natom>1)Then

--natom;vjv+T;

Endlf

vjv+plast;natom++;

ElseIf=T

vjv+(natom-1)x'.'

nalt++;

ElseIf='*'or'+'or'?

'

VJV+斶;ElseIf='('

If(natom>1)Then

--natom;vjv+'.';

EndIf

push(natom,nalt);naltJ0;natomJ0;

ElseIf=')'

vJv+(natom-1)x'.'vjv+naltx'「

pop(natom,nalt);natom++;

Endlf

Endwhile

vjv+(natom-1)x'.'

vjv+naltxT

Returnv;

3构造NFA

有多种方式用来从正则表达式构造NFA,最常见的两种,也是实践中经常使用的是

Thompson构造法和Glushkov构造法。

Thompson方法简单,并且构造的NFA中状态数量(最多2m个)和转移数量(最多4m个)都是线性的。

这种自动机存在&转移,即空转移。

Thompson自动机

Thompson自动机构造的核心思想是先形成正则表达式RE对应的树表示Tre,然后自底

向上地对树的每个节点v,构造一个自动机Th(v)来识别以v为根的子树所表达的语言。

根据不同类型的中间节点和叶节点,有不同的自动机构造方法,具体情况如下。

空字的构造方法。

自动机由连接两个节点而组成

单字符a的构造方法,与空字类似,只不过转移是使用字符来标识,而不是使用空字符

串。

冲箪7F#

相连节点的构造法。

将两个子节点vi和vr对应的Thompson自动机合并,即第一个自动机的终止状态成为第二个自动机的初始状态。

联合节点的构造法。

对于联合节点,则必须通过子节点对应的自动机Th(vi)和Th(vr)中

的一个。

这时需要&转移。

构造过程中,必须添加两个新的状态:

一个是初始状态I,从它

有两个&转移分别到自动机Th(vi)和Th(vr)的初始状态;另一个是终止状态F,从自动机Th(vi)和Th(vr)的终止状态分别由&转移到达终止状态F。

它表达的语言是REvl|REvr。

星节点的构造方法,它使用了同联合节点构造方法相同的思想。

首先,因为对于语言

REv*,节点v的唯一子节点v*可以被重复任意多次,所以需要创建一个从自动机Th(v*)的终

止状态指向其初始状态的&转移。

但是星符号也意味着自动机Th(v*)可以被忽略。

因此需要

创建初始节点I和终止节点F,并用一个8转移把它们连接起来。

另外,再创建两条&转移

分别用来从节点I指向Th(v*)的初始状态以及从Th(v*)的终止状态指向F。

最终,自动机识别的语言是(REv*)*。

2个状态和4个转移。

因此,

和4m个。

下面图表示了正则表达式

整个Thompson算法包含自底向上的树的遍历,同时保证根节点开始构造的自动机即为

能够表示整个正则表达式的Thompson自动机。

在构造树表示中的每一个节点时,自动机中相应地最多增加

构造完成后,状态与转移的数量最多为2m

(AT|GA)((AG|AAA)*)的构造过程。

 

Thompson算法由函数post2nfa实现。

post2nfa函数输入一个数组表示的解析树,返回Thompson自动机,它使用了下面两种数据结构。

typedefstruct_state

{

intc;表示状态的特性,小于256时,表示此状态输入c将转移到out指向的状态

struct_state*out;下一个状态

struct_state*out1;c是Split时,指向下一个分支状态

intlastlist;

intstateid;状态编号

}State;

typedefunion_ptrlist

{

union_ptrlist*next;

State*s;

}Ptrlist;

typedefstruct_frag

{

State*start;

Ptrlist*out;

}Frag;

Frag结构是一个部分NFA,start指向NFA的开始状态,out指向一系列位置,这些位置需要被设置成这个部分NFA的下一个状态。

函数中使用了一个Frag的栈来保存NFA的

片段。

stackp=stack;for(p=postfix;*p;p++){

switch(*p){

default:

单字符的构造

/*生成一个新的状态s*/s=state(g,*p,NULL,NULL);

/*生成一个新的Frag,用s作为start状态,它的out作为终止状态,压入栈

中*/

push(frag(s,list1(&s->out)));

break;

case'.'+256:

相连节点的构造

e2=pop();

e1=pop();

/*连接两个相邻Frag,第一个的out指向第二个的start状态*/

patch(e1.out,e2.start);

/*生成一个新的Frag,用e1的start状态作为start状态,e2的out作为终止状态,压入栈中*/

push(frag(e1.start,e2.out));break;

caseT+256:

联合节点的构造

e2=pop();

e1=pop();

/*生成一个新的Split状态s,指向el和e2的start状态*/

s=state©Split,el.start,e2.start);

/*生成一个新的Frag,用Split的start状态作为start状态,终止状态为el和e2

的终止状态的连接,压入栈中*/

push(frag(s,append(e1.out,e2.out)));

break;

case?

+256:

问号节点的构造

e=pop();

/*生成一个新的Split状态s,指向e的start状态和

&转移*/

s=state(g,Split,e.start,NULL);

/*生成一个新的Frag,用Split的start状态作为start状态,终止状态为e和s的终止状态的连接,压入栈中*/

push(frag(s,append(e.out,Iist1(&s->out1))));break;

case'*'+256:

星节点的构造

e=pop();

/*生成一个新的Split状态s,指向el的start状态和s=state(g,Split,e.start,NULL);

/*e的下一个状态回指

s*/

8转移*/

patch(e.out,s);

/*生成一个新的终止状态,压入栈中

Frag,用Split的start状态作为

*/

start状态,终止状态为

push(frag(s,list1(&s->out1)));break;

case'+'+256:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1