正则表达式(a?)*不是指数?

Jar*_*mex 9 regex theory pcre finite-automata backtracking

我目前正在研究正则表达式的问题,当与某个输入匹配时,它可能最终在指数时间内运行,例如两者(a*)*并且当与字符串aaaaab匹配时(a|a)*可能表现出" 灾难性的回溯 " - 对于每个额外的"a"匹配字符串,尝试匹配字符串双精度所需的时间.仅当引擎使用回溯/ NFA方法尝试在失败之前尝试树中的所有可能分支(例如PCRE中使用的分支)时,才会出现这种情况.

我的问题是,为什么不(a?)*脆弱?根据我对回溯的理解,字符串"aaaab"中应该发生的事情本质上就是这样(a|a)*.如果我们使用标准的Thomspson NFA结构构建NFA,那么对于每次发生的epsilon转换,引擎必须继续采用它们并以与两个a的情况相同的方式进行回溯?例如(省略一些步骤,@替换epsilon):

"aaaa"匹配,但不能匹配'b',失败(回溯)
"aaaa @"匹配,'b'失败(回溯)
"aaa @ a"匹配,'b'失败(回溯)
"aaa @ a @ "匹配,'b'失败(回溯)
......
"@ a @ a @ a @ a @"匹配,'b'失败(回溯)

尝试所有可能的epsilons和a的组合,肯定会导致路线的指数爆炸?

从NFA中移除epsilon过渡是有意义的,但我相信这具有从(a*)*模式中去除所有非确定性的效果.这绝对是脆弱的,所以我不完全确定发生了什么!

非常感谢你提前!

编辑: Qtax已经指出,当传统的回溯遍历NFA时,epsilons仍然无法存在,否则(@)*将尝试永远匹配.那么NFA的实施可能会导致(a*)*并呈现(a|a)*指数,而(a?)*不是如此?这真的是问题的症结所在.

Jar*_*mex 4

好吧,经过一番调查后,我最终发现这归因于 NFA 实现中“障碍”的使用。简而言之,障碍被放置在 NFA 中的战略点上(例如在 a* 的 NFA 构造中紧接在“a”转换之后的节点上)。他们要求比赛从上次击中障碍开始继续进行。这可以防止 NFA 陷入匹配无限数量 epsilon 的情况并允许其终止。

换句话说,不可能从一个障碍物到仅匹配电子移动的同一障碍物 - 如果发生这种情况,路线将被丢弃并从前一点发生回溯。这还有一个副作用,即 (a?)* 不容易受到指数爆炸的影响,因为 a? 第二次无法匹配 null。