【问题标题】:regular expression matching support for '.' and '*' [closed]'.' 的正则表达式匹配支持和'*' [关闭]
【发布时间】:2012-09-18 02:03:20
【问题描述】:

Implement regular expression matching with support for ‘.’ and ‘*’.

‘.’ 匹配任何单个字符。 ‘*’ 匹配零个或多个前面的元素。匹配应该覆盖整个输入字符串(不是部分)。

一些例子:

isMatch(“aa”,”a”) → 假

isMatch(“aa”,”aa”) → 真

isMatch(“aaa”,”aa”) → 假

isMatch(“aa”, “a*”) → 真

isMatch(“aa”, “.*”) → 真

isMatch(“ab”, “.*”) → 真

isMatch(“aab”, “c*a*b”) → 真

作者给出如下解决方案,真的很漂亮。

bool isMatch(const char *s, const char *p) {
  assert(s && p);
  if (*p == '\0') return *s == '\0';

  // next char is not '*': must match current character
  if (*(p+1) != '*') {
    assert(*p != '*');
    return ((*p == *s) || (*p == '.' && *s != '\0')) && isMatch(s+1, p+1);
  }
  // next char is '*'
  while ((*p == *s) || (*p == '.' && *s != '\0')) {
    if (isMatch(s, p+2)) return true;
    s++;
  }
  return isMatch(s, p+2);
}

作者还给出了一些进一步的想法:

如果您仔细考虑,您可以利用上述代码以指数复杂度运行的某些情况。

你能想出一些例子吗?你将如何制作上面的代码 效率更高?

我想出了一个需要很长时间才能得到结果的案例,而 字符串 s 和 p 的长度并不大。

s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" p[] ="a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a *a*a*b"

谁能帮我验证这个答案? 如何看待这种寻找极端的测试题?

【问题讨论】:

  • 提示:在isMatch 函数中添加一些“日志记录”以宣布其进度。从那里你应该能够设计一个通用公式,告诉你你的例子需要多少“步骤”。最终,这是一个数学(计数)问题。 :)
  • @RayToal,是的,我已经做过类似的实验。我定义了一个名为 count 的全局参数。然后在 isMatch 函数中加一。你的意思是这样的吗?
  • 是的,但您可能还想看看自己走了多远,而不仅仅是数数。将诸如当前索引之类的内容记录到字符串中以及递归的深度将使您对算法的工作原理产生有趣的了解。
  • @RayToal,非常感谢您的帮助。我仍然无法弄清楚如何使用“记录”技能,同时我真的很想掌握它,以便我将来可以处理所有这些情况。你能给我更多的提示吗?我不是要你帮我写代码。或者有没有关于“记录”技能的例子?非常感谢您的帮助
  • 哦,我所说的记录只是指普通的旧printf。我会提交一个答案。

标签: algorithm recursion string-matching


【解决方案1】:

要了解您的案例为何表现出指数行为的最佳原因是首先对代码进行一些试验,然后尝试从中收集一些经验数据并做出假设。

首先,让我们添加一些简单的“日志”:

#include <cassert> 
#include <cstdio>
using namespace std;

int count = 0;

bool isMatch(const char *s, const char *p) {
    printf("%5d   %s   %s\n", count++, s, p);  
    .
    .
    .

现在让我们进行一些实验,确保在每次实验之前重置计数(请记住,在实际代码中要避免使用全局变量 :))

isMatch("a", "a*b");
isMatch("aa", "a*a*b");
isMatch("aaa", "a*a*a*b");
isMatch("aaaa", "a*a*a*a*b");
isMatch("aaaaa", "a*a*a*a*a*b");
isMatch("aaaaaa", "a*a*a*a*a*a*b");

您可以查看每个的输出,查看每个生成的行数,然后问自己“当我延长字符串时,递归调用的数量如何增长?” (经典经验算法分析!)

我在这里为您完成了aaa 案例:http://ideone.com/8t2kS

您可以看到它走了 34 步。查看输出;它应该让您对匹配的性质有所了解。并尝试增加长度的字符串。愉快的实验。

【讨论】:

    【解决方案2】:

    这是一个经典案例,通过递归下降实现正则表达式匹配会导致病态行为。

    实现这一点的正确方法是将您的正则表达式转换为非确定性状态机。它需要(相当多)更多代码,但对于任何给定的正则表达式都会以线性时间运行。

    这里有一个关于这个主题的first class article

    干杯!

    【讨论】:

      猜你喜欢
      • 2013-04-16
      • 1970-01-01
      • 2013-07-08
      • 1970-01-01
      • 2013-01-23
      • 2021-12-11
      • 2021-02-13
      • 2011-08-17
      相关资源
      最近更新 更多