我正在从教科书中处理一些正则表达式问题,并且其中的内容如下:
“ [[匹配]所有以整数开头并以单词结尾的字符串。”
我为此编写了以下正则表达式:
^[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”的字符串,尽管常数较小,但是增长因子相同。
编辑:我看到的问题是我有.*+
哎呀!仍然我不确定为什么会导致指数行为。
答案 0 :(得分:7)
问题出在我确信您打算使用.*+\b
而不是.*\\b
。
为什么会导致可怕的行为:问题是.*
可以对任意数量的字符进行数学运算,而+
意味着可以匹配任意数量的字符。但是,为了适应POSIX规范,它必须尝试使整个模式匹配尽可能长的字符串。我的猜测是,要这样做,首先是尝试使用.*
来匹配一个字符,然后重复N次。然后尝试.*
匹配两个字符,并重复M次。然后尝试用.*
匹配三个字符,并将它们重复L次(依此类推)。哦,请注意,也不必使所有.*
模式都匹配相同数量的字符,因此组合的数量呈指数增长。
由于它不知道总体上应该匹配多少个字符,因此它将尝试每种可能的组合,直到到达最后一个,然后发现它们都匹配相同长度的字符串,并声明它整体失败(因为\b
,它是输入字符串中不存在的退格字符)。根据您是使用NFA还是DFA进行正则表达式匹配,您可能会得到观察到的可怕行为,也可能会得到完全线性的行为-或(取决于您执行DFA / NFA转换的方式)无法编译正则表达式(可能不太合规,但仍可能是更好的行为)。
答案 1 :(得分:0)
由于 + ,我认为正则表达式引擎希望仅查找一次。* 。这已经是无穷无尽的了,所以引擎会在一段时间后取消操作。