これは私が同様の問題に使用したものです:
<?php
$input="AACABABCABCABCP";
//Prepare index array (A..Z) - adapt to your character range
$idx=array();
for ($i="A"; strlen($i)==1; $i++) $idx[$i]=array();
//Prepare hits array
$hits=array();
//Loop
$len=strlen($input);
for ($i=0;$i<$len;$i++) {
//Current character
$current=$input[$i];
//Cycle past occurrences of character
foreach ($idx[$current] as $offset) {
//Check if substring from past occurrence to now matches oncoming
$matchlen=$i-$offset;
$match=substr($input,$offset,$matchlen);
if ($match==substr($input,$i,$matchlen)) {
//match found - store it
if (isset($hits[$match])) $hits[$match][]=$i;
else $hits[$match]=array($offset,$i);
}
}
//Store current character in index
$idx[$current][]=$i;
}
print_r($hits);
?>
Nは文字列の長さ、Mは文字範囲の幅で、O(N * N / M)時間だと思います。
それはあなたの例の正解だと私が思うものを出力します。
編集:
このアルゴには、実行中に有効なスコアを保持するという利点があるため、バッファリングを介して確認できる限り、ストリームに使用できます。これは効率的に報われます。
編集2:
繰り返し検出に最大長を許可すると、スペースと時間の使用量が減少します。if ($matchlen>MAX_MATCH_LEN) ...制限インデックスサイズや文字列比較長などを使用して、過去の発生を「早期に」排除します。