为什么Regex(c++)需要指数时间

Why is Regex (c++) taking exponential time?

本文关键字:指数时间 c++ Regex 为什么      更新时间:2023-10-16

我正在做一些教科书中的正则表达式问题,其中包括以下内容:

"[匹配]以整数开头、以单词结尾的所有字符串。">

我为此写了以下正则表达式:

^[0-9]+s.*+b[a-zA-Z]+$

然而,当我用以下代码在C++中实现这一点时:

#include <iostream>
#include <string>
#include <regex>
#include <time.h>
int main(){
clock_t t;
bool match;
std::string exp = "^[0-9]+\s.*+b[a-zA-Z]+$";
std::string str = "1 a few words 1";
std::string s (str);
std::smatch m;
std::regex e (exp);
while (true){
t = clock();
match = std::regex_match(s, m, e); 
s = s + "1";
std::cout << clock() - t << std::endl;
}   
}

每次迭代花费的cpu时间为:

1 1181529
2 3398674
3 10102763
4 30370932
5 92491242

看起来它的复杂性是O( 3^n )

为什么会这样?这个表达是不是我做错了什么?

如果我使用像"1 a 1"这样的字符串,尽管常数较小,但增长因子是相同的。

编辑:我看到的问题是我有一个.*+oops!不过,我不确定为什么这会导致指数行为。

问题在于使用.*+b,而不是我很确定您想要的.*\b

至于为什么这会导致可怕的行为:问题是.*可以计算任意数量的字符,而+意味着匹配任意数量的这些字符。但是,为了符合POSIX规范,它必须尝试使整个模式匹配尽可能长的字符串。我的猜测是,要做到这一点,首先要尝试使用.*来匹配一个字符,并重复它N次。然后,它尝试使用.*匹配两个字符,并重复该操作M次。然后尝试使用.*匹配三个字符,并将它们重复L次(依此类推(。哦,请注意,它也不必让所有的.*模式都匹配相同数量的字符,所以组合的数量呈指数级增长。

由于它不知道总共应该匹配多少个字符,所以它会尝试所有可能的组合,直到到达最后一个,发现它们都匹配相同长度的字符串,并宣布这是一个整体失败(因为您有一个b,它是一个空白字符,不存在于输入字符串中(。根据你是使用NFA还是DFA进行正则表达式匹配,你可能会得到你观察到的可怕行为,也可能会得到完全线性的行为——或者(取决于你如何进行DFA/NFA转换(它可能只是无法编译正则表达式(这可能不太符合,但仍然可能是更好的行为(。

我认为正则表达式引擎只想找到任何.*一次,因为+。这已经是没完没了的了,所以发动机过了一段时间就取消了操作。