使用基于 DFA(线性时间)的正则表达式捕获组:可能吗?
Posted
技术标签:
【中文标题】使用基于 DFA(线性时间)的正则表达式捕获组:可能吗?【英文标题】:Capture groups using DFA-based (linear-time) regular expressions: possible? 【发布时间】:2015-05-10 13:47:55 【问题描述】:是否可以使用基于 DFA 的正则表达式实现捕获组,同时保持与输入长度相关的线性时间复杂度?
直觉上我认为不是,因为子集构建过程不知道它可能落在了哪个捕获组中,但这是我第一次意识到这可能是一个潜在的问题,所以我不知道。
【问题讨论】:
你的意思是仅仅捕获组,还是匹配反向引用? @Bergi:抓拍就行了。 【参考方案1】:我的http://github.com/hoehrmann/demo-parselov 就是这样做的。我目前没有解释网页上的构造,但假设你有一个像
这样的语法X = "a" B "c"
B = "b"
您可以将此常规语法转换为带有标记顶点的图
-
开始 X
“一个”
开始B
“b”
最终B
“c”
最终 X
DFA 状态对应于这些顶点的集合。第一个由顶点 1 和 2 组成,第二个由顶点 3 和 4 组成,然后是 5 和 6,最后是 7。如果您解析字符串“abc”,则有
-
偏移量:0,顶点:[1, 2]
偏移量:1,顶点:[3, 4]
偏移量:2,顶点:[5, 6]
偏移量:EOF,顶点:[7]
这也是一个图表。您可以使用 (offset, vertex) 对作为顶点写出边:
-
(o0, v1) -> (o0, v2)
(o0, v2) -> (o1, v3)
(o1, v3) -> (o1, v4)
...
这样的图可能包含最终不会到达最终顶点(EOF,v7)的顶点,但这样的顶点可以在 O(n) 时间内消除。如果语法不明确,则匹配将是通过结果图的路径。可能有很多可能的路径。
【讨论】:
【参考方案2】:是否可以使用基于 DFA 的正则表达式实现捕获组,同时保持与输入长度相关的线性时间复杂度?
是的 - 至少在捕获组是确定性的情况下。考虑示例正则表达式/a|(a)/
;将其与输入 "a"
匹配可以生成捕获的组,也可以不生成。
我认为捕获组可以基于使用finite state transducers 的理论基础,它类似于自动机,但也可以在更改状态时输出字符串。您可以回显输入,但例如用括号括住每个捕获组。
直觉上我认为不是,因为子集构建过程不知道它可能落在了哪个捕获组中,但这是我第一次意识到这可能是一个潜在的问题,所以我不知道。
确实,这是个问题。我认为您可以通过使用捕获状态标记您的集合来解决它,并同样区分结果 DFA 的状态。您可能无法为上述正则表达式生成完全确定性自动机,因为Wikipedia writes:“一些非确定性转换器不承认等效的确定性转换器”。
但是,子集构造过程的修改是可能的,请参阅Determinization of Transducers。他们的算法似乎围绕以下内容:
局部歧义 […] 通过尽可能延迟输出来解决,直到可以确定地写出这些符号。
例如,正则表达式/ab|(a)c/
甚至/(a[bc])|ad/
都可以解析为确定性转换器。请注意,它们的内存表示可能比没有捕获组时大得多。
【讨论】:
我试图了解确定性是否足够。是否可以为x*(a*b)c|x*(a?)(a?)bd
之类的模式制作 DFA?如果您延迟捕获直到看到c
或d
,那么您已经解决了歧义问题,但是您如何知道应该在哪里开始和结束捕获呢?有没有办法在相对于输入长度的恒定时间(希望是恒定空间)内做到这一点?也许使用堆栈或其他东西?
嗯,好问题。我还没有深入研究有限状态传感器。我认为您可以通过按后缀区分捕获组来解决这个问题,即abc
上的/x*(a*b)c|x*(a*b)d/
应该输出(ab0)c
和xabd
上应该输出x(ab1)d
。这些换能器可以确定为相当简单的形式。请注意,您将始终需要输入长度的线性时间;输出(空间)也将是线性的,但如果您优化并使用指向输入的指针,您应该得到线性空间 wrt 捕获组的数量。
我明白了。请注意,恒定时间是指每个状态转换时的输入长度恒定(因为有 n 个状态转换)。关于捕获,这是用户指定的内容,所以如果我可以更改它们并稍后快速恢复用户想要的内容。
对 - 但由于输出需要线性时间,我猜状态转换所花费的时间摊销为常数。以上是关于使用基于 DFA(线性时间)的正则表达式捕获组:可能吗?的主要内容,如果未能解决你的问题,请参考以下文章