【问题标题】:Algorithm for regular expressions - combinations on or正则表达式的算法 - 或组合
【发布时间】:2015-09-19 02:05:41
【问题描述】:

我正在开发一个 C++ 应用程序来首先解析正则表达式字符串,然后用它执行一些计算。是否有任何现有的算法可以输出长度为 L 的字符串的数量 N,这些字符串可以被给定的正则表达式(例如 (a|ab)* | (aa|bb)*)识别?或者是否有我可以使用的数学公式,例如涉及阶乘的数学公式?我只想获取给定数字 L 的此类正则表达式短语可以识别的字符串数 N。(a|ab)* 的示例正则表达式可以识别多少长度为 5 (L) 的字符串。我认为答案是 5。但是对于大量的 L,我想知道是否有任何算法或数学表达式可以计算出来。

【问题讨论】:

  • (something)*匹配的字符串个数?严重地?它是无限的。
  • 您需要更新您的正则表达式或给出实际字符串的示例,您可能误解了模式匹配。
  • @ergonaut 你是在跟我说话还是 te7?如果 te7 给我们提供实际的字符串会有什么帮助?
  • @devianfan 也许他的意思是 (a|ab)?否则你是对的,算法会输出 a、aa、aaa、aaaa.. 并且永远不会到达 'ab's。
  • 如果有人想尝试解决这个问题,同样的问题是hackerrank的挑战:hackerrank.com/challenges/count-strings

标签: c++ regex algorithm


【解决方案1】:

这是一种基于矩阵求幂的高效算法,可用于计算这些数字。

我只提供高级描述,而不是代码。

  1. 首先,您想使用计算机科学基础中众所周知的等价法,即(简单)正则表达式等价于有限状态机。

    (回想一下,有限状态机本质上是一个流程图,其中从每个节点到字母表中的每个字母到某个特定的其他节点(或者可能是一个循环)都有一个标记的边缘。此外,一些状态的子集称为“接受集”,流程图中的某些特定状态是开始状态。据说一个字符串在有限状态机中诱导一条路径,通过从开始状态开始并跟随标记的边缘如果最终状态在接受集中,则机器accepts 为字符串,否则为rejects 字符串。 一个经典的构造表明,我们可以从任何正则表达式构造一个大小相似的有限状态机,并且从任何有限状态机我们都可以构造一个大小相似的正则表达式。任何与正则表达式对应的语言(所有有限字符串的子集)都称为“正则”,当且仅当它对应于有限状态机时,语言才是正则的。)

    例如,如果我有一个字母{a,b,c},和正则表达式(a|b)*,它对应一个机器有两种状态。开始状态有一个标记为a 的循环,一个标记为b 的循环和一个标记为c 的箭头指向第二个状态。第二个状态本身有三个循环,所以如果你去那里你会被困住。接受集仅包含开始状态。

    程序的第一步是将正则表达式转换为相应的有限状态机。 (可能是一些现有的正则表达式库已经基本做到了,我认为 PCRE 可能,虽然我不确定。)

  2. 给定一个有限状态机,我想构造一个对应的随机矩阵。在这个矩阵中,每个状态都有一行,每个状态都有一列,每个条目都是一个概率。进入(i,j) 处的概率p_{i,j} 等于如果我处于状态i 并且我读到一个随机字母,我接下来会进入状态j 的概率。所以对于我给出的例子,矩阵是

    [ 2/3 1/3 ]
    [0 1]

  3. 如果你想知道长度为k的字符串,那么使用矩阵求幂,计算矩阵M^k,其中M是上面的概率转移矩阵。

  4. 最后,如果q 是开始状态,则将接受集中每个状态s 的所有条目M^k_{q, 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

注意:正如 cmets 中所指出的,对于任何固定的正则表达式,此算法在 k 的位数上是多项式的,因此即使对于较大的 k 也很好。尽管正则表达式的大小在最坏的情况下是指数级的——为了处理大型和复杂的正则表达式,我猜如果你想使用这种方法,你应该使用某种 DFA 最小化。对于问题中显示的简单正则表达式,我认为应该没问题。

【讨论】:

  • 1/3 和 2/3 从何而来?您是否假设 b 的可能性是 a 的两倍? (PCRE 不创建 FSM。RE2 使用记忆 DFA 算法,避免指数 DFA 爆炸。Flex 和 Ragel 构建真正的 DFA;Ragel 有一个我从未看过的 XML 输出选项,但它可能有用。不像Flex,Ragel 进行状态最小化。Regex->DFA 库分散在网络周围;其中一些有效。)
  • 对不起,我只是算术不及格。出于某种原因,我认为我认为有一个c,不能让我的例子保持直截了当。我现在更改了示例。
  • 酷。我不确定随机矩阵是否有助于理解;就个人而言,我只是计算从 i->j 的转换(这与计算概率并乘以字母大小相同,因为转换概率是触发该转换的符号数除以字母大小)。无论如何,这以你的最后一段结束。使该算法成指数(在正则表达式的长度上)的原因是 DFA 状态计数在正则表达式的长度上是最坏情况下的指数。但通常,它应该足够快。无论如何,我没有更好的:)
  • @rici:感谢您指出这一点,我想我忘记了转换的一些细节。 iirc,从正则表达式到 NFA(非确定性有限自动机)是多项式,从 NFA 到正则表达式是多项式。但是从 NFA 到 DFA 是指数级的(不可避免)。不知道有没有很好的方法来调整算法,让你可以直接在NFA转换矩阵上做点什么,听起来有点希望
  • 我没有足够的数学参考来理解 (NM)^k 如何给出长度为 k 的字符串的计数?一些指针肯定会有所帮助?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多