你的问题是递归......你知道吗,忘记递归!=p 它在 PHP 中不会很好地工作,没有它算法也很清晰。
function find_repeating_sequences($s)
{
$res = array();
while ($s) {
$i = 1; $pat = $s[0];
while (false !== strpos($s, $pat, $i)) {
$res[$pat] = 1;
// expand pattern and try again
$pat .= $s[$i++];
}
// move the string forward
$s = substr($s, 1);
}
return array_keys($res);
}
出于兴趣,我也用 PHP写了Tim 的答案:
function find_repeating_sequences_re($s)
{
$res = array();
preg_match_all('/(?=(.+).*\1)/', $s, $matches);
foreach ($matches[1] as $match) {
$length = strlen($match);
if ($length > 1) {
for ($i = 0; $i < $length; ++$i) {
for ($j = $i; $j < $length; ++$j) {
$res[substr($match, $i, $j - $i + 1)] = 1;
}
}
} else {
$res[$match] = 1;
}
}
return array_keys($res);
}
我让他们在一个 800 字节随机数据的小型基准测试中解决了这个问题:
$data = base64_encode(openssl_random_pseudo_bytes(600));
每个代码运行 10 轮并测量执行时间。结果?
Pure PHP - 0.014s (10 runs)
PCRE - 40.86s <-- ouch!
当您查看 24k 字节(或实际上超过 1k 的任何内容)时,它会变得更奇怪:
Pure PHP - 4.565s (10 runs)
PCRE - 0.232s <-- WAT?!
事实证明,正则表达式在 1k 个字符后失效,因此$matches
数组为空。这些是我的 .ini 设置:
pcre.backtrack_limit => 1000000 => 1000000
pcre.recursion_limit => 100000 => 100000
我不清楚只有 1k 个字符后如何达到回溯或递归限制。但即使这些设置以某种方式“固定”,结果仍然很明显,PCRE 似乎不是答案。
我想用 C 写这个会加快速度,但我不确定到什么程度。
更新
在hakre 的回答的帮助下,我整理了一个改进版本,在优化以下内容后将性能提高了约 18%:
移除substr()
外循环中的调用以推进字符串指针;这是我以前的递归化身的遗留物。
将部分结果用作正缓存以跳过strpos()
内部循环内的调用。
在这里,它的所有荣耀(:
function find_repeating_sequences3($s)
{
$res = array();
$p = 0;
$len = strlen($s);
while ($p != $len) {
$pat = $s[$p]; $i = ++$p;
while ($i != $len) {
if (!isset($res[$pat])) {
if (false === strpos($s, $pat, $i)) {
break;
}
$res[$pat] = 1;
}
// expand pattern and try again
$pat .= $s[$i++];
}
}
return array_keys($res);
}