正则表达式(c ++)为什么要花费指数时间?

时间:2018-07-17 20:16:29

标签: c++ regex time-complexity clock

我正在从教科书中处理一些正则表达式问题,并且其中的内容如下:

“ [[匹配]所有以整数开头并以单词结尾的字符串。”

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

^[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”的字符串,尽管常数较小,但是增长因子相同。

编辑:我看到的问题是我有.*+哎呀!仍然我不确定为什么会导致指数行为。

2 个答案:

答案 0 :(得分:7)

问题出在我确信您打算使用.*+\b而不是.*\\b

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

由于它不知道总体上应该匹配多少个字符,因此它将尝试每种可能的组合,直到到达最后一个,然后发现它们都匹配相同长度的字符串,并声明它整体失败(因为\b,它是输入字符串中不存在的退格字符)。根据您是使用NFA还是DFA进行正则表达式匹配,您可能会得到观察到的可怕行为,也可能会得到完全线性的行为-或(取决于您执行DFA / NFA转换的方式)无法编译正则表达式(可能不太合规,但仍可能是更好的行为)。

答案 1 :(得分:0)

由于 + ,我认为正则表达式引擎希望仅查找一次。* 。这已经是无穷无尽的了,所以引擎会在一段时间后取消操作。