关于正则引擎ε-NFA -> NFA (仅通过边建立限制结束状态的两种尝试

本文探讨了在正则引擎中如何正确判断一个状态是否为结束状态,尤其是在ε-NFA转换为NFA的过程中。文章通过分析不同方案的优缺点,指出了一开始的方案和去除非ε边限制的方案存在的问题,并提出了一种正确的方法,即在构造完成后,将能通过ε边到达结束状态的状态标记为结束状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于自动机 一个状态是否为结束状态的判断


1一开始的方案


对于任何状态,设置初始状态为 true (这里的true为是否为结束状态),每当向后连接边的时候(即以状态为startstatus构造边时候)

构造非ε边的,把边的startstatus设置为false。构造ε边则不进行设置直接进行连接

这样做会产生一个错误

对于单个串联并联重复(紫色箭头表示最终去ε边后的NFA)  




这样做可行,因为当这三个图的去掉ε边最终指向的end均为true


然而当多个规则进行串联的时候,譬如对于a*b    单个重复规则与一个字符串联



对于上述图进行消去ε边得到的图  匹配a自环边的状态本应当为false 这里变成了true 也就是最终的正则规则变成了(a*|a*b)


如果仅仅从构造E边得时候进行限制 ,也就是与下一个规则连接的状态   与最终去ε边NFA边指向的状态并非一个的时候

这时候就会产生错误。

构造边的代码

Edge* NFA::make_edge(Status* status1, _MatchContent content, Status* status2,bool isAdd)
{
	if (isAdd&&content.left!=-1&&status1->IsFinal)                      //-1是是否为ε边的标志
		status1->IsFinal = false;                                           //将startstatus设置为false
	auto edge = new Edge(status1, content, status2);

	if (isAdd)                                  
	{
		if (!_isStatusExist(status1))
			add_status(status1);
		if (!_isStatusExist(status2))
			add_status(status2);
		add_edge(edge);
	}
	return edge;                                       
}


2把构造非ε边的限制去掉

也就是去掉if中content.left!=-1的判断 对于所有新建立的边,我们将其开始状态设置为false

Edge* NFA::make_edge(Status* status1, _MatchContent content, Status* status2,bool isAdd)
{
	if (isAdd&&status1->IsFinal)                      //-1是是否为ε边的标志
		status1->IsFinal = false;                                           //将startstatus设置为false
	auto edge = new Edge(status1, content, status2);

	if (isAdd)                                  
	{
		if (!_isStatusExist(status1))
			add_status(status1);
		if (!_isStatusExist(status2))
			add_status(status2);
		add_edge(edge);
	}
	return edge;                                       
}

对于单个并联来讲就已经产生错误,因为并联消去ε边指向的并非最终状态。见上文图

于是对于并联节点的构造

pair<Status*, Status*> NFA::gen_and(Node* node)             //处理and
{
	And_Node* and_node = static_cast<And_Node*>(node);
	Status* s_start = nullptr;
	Status* s_end = nullptr;
	for (auto s : *(and_node->pool))   // 遍历这个  pool
	{
		auto tmp = gen_status(s);     //读出每个node
		if (!s_end)                 //如果没有结束
		{
			s_start=tmp.first;
			s_end = tmp.second;
			continue;
		}
		s_end->IsFinal = false;
		make_edge(s_end,tmp.first);  //就把他们连接起来
		s_end = tmp.second;
	}
	s_end->IsFinal= true;               //然后给最后一个为true
	return make_pair(s_start, s_end);
}
添加一个特殊的节点设置,是的消去ε边指向节点为true。这两可以解决并联的内部问题。和a*b的问题

但实际上,对于多个并联之后的串联 或者单纯的a*又会造成问题

在消去ε边之后,并联之后的串联会同样有上述多出来结束状态问题,a*b解决,a*没有结束状态

因此,单单通过建立边确立正确结束状态是十分困难的

3应当正确的方法

vzch文档上写:如果存在一个有效状态可以仅通过E边到达结束状态,那么这个状态应该被标记为结束状态


一种解决方法是:在构造nfa的过程中,不考虑是否为结束状态。 在构造完成后,将整体的结束状态finish设置为true,(其他均为false)

对于结束状态,寻找能仅通过ε边到达结束状态的状态的反向闭包(与消去ε边方式寻找闭包的方向相反,不知道怎么叫),将这些状态设置为结束状态。


cknightx本人: 在构造nfa的过程中,不考虑是否为结束状态。 在构造完成后,将整体的结束状态finish设置为true,(其他均为false)


在消去ε边过程中,对每个有效状态寻找ε闭包,如果闭包中有结束状态,则将其设置为结束状态

部分图片参考自vczh《构造可配置词法分析器》和《正则表达式》

文章源代码参考cknightx 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值