我正在开发一个C ++应用程序来首先解析正则表达式字符串,然后用它执行一些计算。是否有任何现有的算法可以输出长度为L的字符串数N,这些字符串可以被给定的正则表达式(a|ab)* | (aa|bb)*
识别?或者是否有一个我可以使用的数学公式,例如涉及阶乘的公式?我只想得到这些正则表达式短语对于给定数字L可以识别的字符串数N.例如(a|ab)*
正则表达式可以识别多少长度为5(L)的字符串。我认为答案是5.但是对于大量的L,我想知道是否有任何算法或数学表达式可以计算出来。
答案 0 :(得分:8)
这是一种基于矩阵求幂的高效算法,可用于计算这些数字。
我只会提供高级别的描述,而不是代码。
首先,您想要使用计算机科学基础的一个众所周知的等价物,即(简单的)正则表达式等同于有限状态机。
(回想一下,有限状态机本质上是一个流程图,其中从每个节点开始,字母表中的每个字母都有一个标记的边缘到某个特定的其他节点(或者它的一个循环)。状态子集称为" Accept Set",流程图中的某个特定状态是起始状态。一个字符串被称为在有限状态机中通过从起始状态开始引起路径并且连续跟随标记的边。如果最终状态在接受集中,则机器accepts
为字符串,否则为rejects
字符串。
经典结构表明,从任何正则表达式我们可以构造一个类似大小的有限状态机,并且从任何有限状态机我们可以构造一个类似大小的正则表达式。任何与正则表达式相对应的语言(所有有限字符串的子集)都被称为" regular"当且仅当语言对应于有限状态机时,语言才是常规的。)
例如,如果我有一个字母{a,b,c}
和正则表达式(a|b)*
,它对应一个有两种状态的机器。起始状态有一个标记为a
的循环,一个标记为b
的循环,以及一个标记为c
到第二个状态的箭头。第二个状态有三个循环,所以如果你去那里就会陷入困境。接受集仅包含开始状态。
程序的第一步是将正则表达式转换为相应的有限状态机。 (可能是某些现有的正则表达式库基本上已经这样做了,我认为PCRE可能,虽然我不确定。)
给定一个有限状态机,我想构建一个相应的随机矩阵。在这个矩阵中,每个状态都有一行,每个状态有一列,每个条目都是概率。条目p_{i,j}
的概率(i,j)
等于我在状态i
的概率,并且我读了一个随机字母,我转到状态{{1} } 下一个。所以对于我给出的例子,矩阵是
[2/3 1/3]
[0 1]
如果您想了解长度j
的字符串,然后使用矩阵求幂,请计算矩阵k
,其中M^k
是上面的概率转移矩阵。
最后,如果M
是开始状态,请为接受集中的每个州q
添加所有条目M^k_{q, s}
。这些概率的总和恰好等于正则表达式接受长度为s
的随机字符串的概率。因此,您可以通过乘以k
得到此类字符串的数量,其中N^k
是字母表中的字母数。
我认为这种算法的存在并不难,但它也不是微不足道的,我曾经在计算类理论的期末考试中给出了一个更难的版本作为额外的学分问题。我不知道它的任何现有实现,有兴趣知道。
当你使用矩阵求幂时,你可以通过这种方式对天真的方法进行一些显着的加速。这使您可以快速完成大N
。
我不知道是否有更高效,近似的解决方案,这会很有趣。我想随机抽样总会给你一些东西,但可能会有某种光谱算法,基于矩阵k
或其他东西的奇异值分解。
注意:如果你真的想要实现它,我想你不应该使用浮点数,矩阵M
实际上应该是一个整数矩阵。基本上你会将它乘以M
,其中N
是字母表中的字母数。你可以稍后跳过乘以N
的总和。我认为使用概率更容易理解,但在该版本中,N^k
只会计算M^k_{i,j}
。
注意:正如评论中所指出的,对于任何固定正则表达式,此算法是number of paths from i to j of length k
的位数的多项式,因此即使对于大k
它也是如此。尽管在正则表达式的大小中,它在最坏的情况下是指数 - 为了处理大而复杂的正则表达式,你应该使用某种DFA最小化我想如果你想使用这个方法。对于问题中显示的简单正则表达式,我认为应该没问题。