Implement regular expression matching with support for ‘.’ and ‘*’.
'。'匹配任何单个字符。 '*'匹配前面元素的零个或多个。匹配应覆盖整个输入字符串(非部分)。
一些例子:
isMatch(“aa”,“a”)→false
isMatch(“aa”,“aa”)→true
isMatch(“aaa”,“aa”)→false
isMatch(“aa”,“a *”)→true
isMatch(“aa”,“。*”)→true
isMatch(“ab”,“。*”)→true
isMatch(“aab”,“c * a * b”)→true
作者给出了以下解决方案,非常漂亮。
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 [] =“aaaaaaaaaaaaaaaaaaaaaaaaaaaaa” 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“
任何人都可以帮我验证这个答案吗? 如何看待这种极端测试问题?
答案 0 :(得分:2)
这是一个经典案例,通过递归下降实现正则表达式匹配会导致病态行为。
实现此目的的正确方法是将正则表达式转换为非确定性状态机。它需要(相当多)更多代码,但对于任何给定的正则表达式都将以线性时间运行。
这是关于这个主题的first class article。
干杯!
答案 1 :(得分:2)
理解为什么你的案例表现出指数行为的最好原因是首先试验一下代码,然后尝试从中收集一些经验数据并做出假设。
首先,让我们添加一些简单的“日志记录”:
#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步。看看输出;它应该让你深入了解匹配的本质。并尝试增加长度的字符串。快乐的实验。