当你阅读Regex: NFA and Thompson's algorithm这样的帖子时,一切看起来都比较直白,直到你在现实生活中意识到你不仅需要像“7”或“b”这样的直接字符,而且还需要:
[A-Z]
[^_]
.
即字符类(或范围)。因此我的问题 - 如何使用字符范围构建NFA?使用“非A”,“其他”等元字符,然后计算重叠范围?这将导致在使用最终自动机时使用树状结构,而不仅仅是表格。
更新:请假设大小不一样(>>> 256)字母。
我在询问NFA,但后来我想将NFA转换为DFA。
答案 0 :(得分:4)
最简单的方法是:
使用细分作为NFA和DFA中转换的标签。例如,范围[a-z]将被重新表示为段[97, 122]
;单个字符' a'会成为[97,97]
;和任何角色'。'将成为[minCode, maxCode]
。
每个否定范围[^ a-z]将导致从开始状态到下一状态的两个转换。在此示例中,应创建两个转换[minCode, 96]
和[123, maxCode]
。
当通过枚举所有可能的字符[abcz]来表示范围时,应创建每个字符的转换,或者代码将第一组字符组成范围以优化转换的数量。所以[abcz]会变成[a-c]|z
。因此有两个过渡而不是四个。
这对NFA来说应该足够了。但是,当存在具有相交字符范围的转换时,将NFA转换为DFA的经典power set construction将不起作用。 要解决此问题,只需要一个额外的泛化步骤。一旦创建了一组所有输入符号,在我们的例子中它将是一组段,它应该被转换成一组非相交段。这可以在时间 O(n * Log(n))中完成,其中n是使用优先级队列(PQ)的集合中的段的数量,其中段由左组件排序。例如:
Procedure DISJOIN:
Input <- [97, 99] [97, 100] [98, 108]
Output -> [97, 97] [98, 99], [100, 100], [101, 108]
第2步。要从&#34;设置状态&#34;创建新的转换。算法应修改如下:
for each symbol in DISJOIN(input symbols)
S <- empty set of symbols
T <- empty "set state"
for each state in "set state"
for each transition in state.transitions
I <- intersection(symbol, transition.label)
if (I is not empty)
{
Add I to the set S
Add transition.To to the T
}
for each segement from DISJOIN(S)
Create transition from "set state" to T
为了在搜索转换和输入符号C时加快匹配,每个状态的转换可能按段进行排序并应用二进制搜索。