Обнаружение последовательных повторяющихся паттернов в строке

Я пытаюсь найти максимальное количество повторений подстроки внутри строки, вот несколько примеров:

"AQMQMB" => QM (2x)
"AQMPQMB" => <nothing>
"AACABABCABCABCP" => A (2x), AB (2x), ABC (3x)

Как вы можете видеть, я ищу только последовательные подстроки, и это кажется проблемой, потому что все алгоритмы сжатия (по крайней мере, я в курсе) не заботятся о последовательности ( LZ *) или слишком просты для обработки последовательных шаблонов. вместо отдельных элементов данных ( RLE). Я думаю, что использование алгоритмов дерева суффиксов также бесполезно из-за той же проблемы.

Я думаю, что есть некоторые алгоритмы биоинформатики, которые могут сделать это, кто-нибудь имеет представление о таком алгоритме?

Правка Во втором примере может быть несколько вариантов последовательных шаблонов (спасибо Eugen Rieck за уведомление, читайте комментарии ниже), однако в моем случае использования любая из этих возможностей на самом деле приемлема.

2 ответа

Решение

Вот что я использовал для аналогичной проблемы:

<?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);

?>

Я подозреваю, что это O(N*N/M) время, где N - длина строки, а M - ширина диапазона символов.

Это выводит то, что я считаю правильными ответами для вашего примера.

Редактировать:

У этого алгоритма есть преимущество сохранения действительных результатов во время работы, поэтому его можно использовать для потоков, если вы можете просматривать их с помощью некоторой буферизации. Это платит за это эффективностью.

Изменить 2:

Если разрешить максимальную длину для обнаружения повторения, это уменьшит использование пространства и времени: исключение слишком "ранних" прошлых вхождений через что-то вроде if ($matchlen>MAX_MATCH_LEN) ... ограничивает размер индекса и длину сравнения строк

Здесь полезны алгоритмы, связанные с суффиксным деревом.

Один из них описан в " Алгоритмах на строках, деревьях и последовательностях" Дэна Гасфилда (глава 9.6). Он использует комбинацию подхода "разделяй и властвуй" и деревьев суффиксов и имеет временную сложность O(N log N + Z), где Z - количество повторений подстроки.

В этой же книге описывается более простой алгоритм O (N2) для этой задачи, также с использованием суффиксных деревьев.

Другие вопросы по тегам