在构建一个简单的正则表达式时,我发现在输入大小增加时,它有一个非常奇怪的性能配置文件。
这是另一个具有类似行为的基本正则表达式:
a+b
我用一个简单的基准来描述它:
Regex regex = new Regex("a+b", RegexOptions.Compiled);
const int maxInputSize = 100;
const int n = 1000;
string input = "";
Stopwatch stopwatch = new Stopwatch();
for (int inputSize = 1; inputSize <= maxInputSize; ++inputSize)
{
input += 'a';
stopwatch.Restart();
for (int i = 0; i < n; ++i)
{
regex.Match(input);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.Ticks);
}
它在字符串“a”,“aa”,“aaa”,...上运行正则表达式,并测量每个字符串长度进行n次匹配所花费的时间。
我知道回溯问题(例如,如果正则表达式类似于(a+a+)+b
),但在这种情况下,即使考虑回溯,我预计线性复杂度
作为一个例子,如果我们想匹配n次'a'这是我天真期望的工作流程:
take first 'a'
take second 'a'
...
take last 'a'
ooops nothing more to take => backtracking
release one 'a' and try to match 'b', nothing => backtracking
...
release second 'a' and retry to match 'b', nothing => backtracking
release first 'a'
ooops we're back at the beginning => no match
因此它应该执行类似 2n 操作的内容。
(本文档似乎证实复杂性应该是线性的:http://msdn.microsoft.com/en-us/library/dsy130b4.aspx)
但我观察到二次复杂度:
所以我的问题是:
提前感谢任何输入。
答案 0 :(得分:4)
Regex.Match
函数搜索子字符串匹配:引擎尝试匹配从字符串的任何索引开始的表达式,给出O(n²)算法。您可以通过将正则表达式锚定到字符串的开头来实现线性性能:
Regex regex = new Regex("^a+b$", RegexOptions.Compiled);