2013-10-14 3 views
14

В этой статье обсуждаются приблизительные методы согласования подстрок, которые используют suffix tree для улучшения времени согласования. Каждый ответ обращается к другому алгоритму.Приблизительное совпадение подстроки с использованием дерева суффикса

  1. Приблизительное соответствие подстроки пытается найти подстроку (шаблон) P в строке T позволяет до k несовпадений.
  2. Чтобы узнать, как создать дерево суффиксов, нажмите here. Однако для некоторых алгоритмов требуется дополнительная предварительная обработка.

Я предлагаю людям добавлять новые алгоритмы (даже если они неполные) и улучшать ответы.

+0

Я добавил, что я могу понять. У меня нет времени. Удачи вам! – Gene

ответ

1

Это был оригинальный вопрос, который начал эту тему.

Профессор Эско Укконен опубликовал paper: Приблизительное сопоставление строк по суффиксу. Он обсуждает 3 различных алгоритмов, которые имеют разные времена соответствия:

  1. Алгоритм A: O(mq + n)
  2. Алгоритм B: O(mq log(q) + size of the output)
  3. Алгоритм С: O(m^2q + size of the output)

Где m длина подстроки, n - длина строки поиска, а q - расстояние редактирования.

Я пытался понять алгоритм B, но у меня возникли проблемы после шагов. Кто-нибудь имеет опыт работы с этим алгоритмом? Было бы весьма полезно оценить пример или псевдо алгоритм.

В частности:

  1. Что size of the output см в терминах дерева суффиксов или входных строк? Конечная фаза вывода перечисляет все вхождения Key(r) в T, для всех состояний r, помеченных для вывода.
  2. Глядя на Алгоритм C, определяется функция dp (страница 4); Я не понимаю, какой индекс i представляет. Он не инициализирован и не кажется, что он увеличивается.

Вот что я верю (я стою, чтобы исправить):

  1. На седьмой странице, мы представили суффикс концепции дерева; состояние фактически является узлом в дереве суффиксов: let root обозначает начальное состояние.
  2. g(a, c) = b где a и b являются узлами в дереве и c является символ или подстроку в дереве. Таким образом, это переход; от a, следуя края представленных c, мы переходим к узлу b. Это называется переход к переходу. Таким образом, для дерева суффикса ниже, g(root, 'ccb') = red node suffix tree for abccb
  3. Key(a) = edge sequence где a представляет узел в дереве. Например, Key(red node) = 'ccb'. Таким образом, g(root, Key(red node)) = red node.
  4. Keys(Subset of node S) = { Key(node) | node ∈ S}
  5. Существует функция суффикс узлов a и b, f(a) = b: для всех (или, возможно, могут существовать) aroot существует характер c, подстрока x и a узел b такой, что g(root, cx) = a и g(root, x) = b. Я думаю, что это значит, на примере дерева суффиксов выше, что f(pink node) = green node где c = 'a' и x = 'bccb'.
  6. Существует сопоставление H, которое содержит узел из дерева суффиксов и пару значений. Значение задается loc(w); Я все еще не уверен, как оценить функцию. Этот словарь содержит узлы, которые не были устранены.
  7. extract-min(H) относится к достижению запись с наименьшим значением в паре (node, loc(w)) от H.

Суть алгоритма, кажется, связано с тем, как loc(w) оценивается. Я построил свое дерево суффикса, используя комбинированный ответ here; однако алгоритмы работают над суффиксом trie (несжатое суффиксное дерево). Поэтому такие концепции, как глубина, необходимо поддерживать и обрабатывать по-разному. В суффиксе trie глубина будет представлять собой длину суффикса; в дереве суффиксов глубина просто представляет глубину узла в дереве.

3

У вас все хорошо. Я не знаком с алгоритмом, но сегодня прочитал эту статью. Все, что вы написали, правильно, насколько это возможно. Вы правы, что некоторые части объяснения предполагают много.

Ваши вопросы

1.Что делает размер выходного см в терминах дерева суффиксов или входных строк? Конечная фаза вывода отображает все вхождения Key (r) в T для всех состояний r, помеченных для вывода.

Выход состоит из максимальных совпадений k-расстояния P в T. В частности, вы получите окончательный индекс и длину для каждого. Так ясно, что это также O (n) (помните, что big-O является верхней границей), но может быть меньше. Это является признаком того, что невозможно создать p-совпадений менее чем за время O (p). Остальная часть времени связана только с длиной шаблона и числом жизнеспособных префиксов, оба из которых могут быть сколь угодно малыми, поэтому размер вывода может доминировать. Рассмотрим k = 0, а вход «a» повторяется n раз с рисунком «a».

2. При взгляде на алгоритм C определена функция dp (страница 4); Я не понимаю, что представляет собой индекс i. Он не инициализирован и не кажется, что он увеличивается.

Вы правы. Это ошибка. Индекс цикла должен быть i. Как насчет j? Это индекс столбца, соответствующий входному символу, обрабатываемому в динамической программе. Это действительно должен быть входной параметр.

Давайте сделаем шаг назад. Таблица Пример таблица на стр. 6 вычисляется слева направо по столбцу с использованием уравнений (1-4), приведенных ранее. Они показывают, что для получения следующего необходимы только предыдущие столбцы D и L. Функция dp - это всего лишь реализация этой идеи вычисления столбца j от j-1. Столбец j D и L называются d и l соответственно. Столбец j-1 D и L: d' и l' - входные параметры функции.

Я рекомендую вам работать через динамическую программу, пока вы ее не поймете. Алгоритм состоит в том, чтобы избежать дублирования вычислений столбцов. Здесь «дубликат» означает «имеющий те же значения в существенной части», потому что это все, что имеет значение. Несущественные части не могут повлиять на ответ.

Несжатое три просто сжатое, с очевидным расширением, чтобы иметь один ребро на символ. За исключением идеи «глубина», это неважно. В сжатом дереве глубина (глубины) - это просто длина строки, которую он называет Key (s) - необходимо получить из корневого узла s.

Алгоритм

Алгоритм просто умная схема кэширования.

Все его теоремы и леммы показывают, что 1) нам нужно только беспокоиться о существенных частях столбцов и 2) существенная часть столбца j полностью определяется жизнеспособным префиксом Q_j. Это самый длинный суффикс ввода, заканчивающийся на j, который соответствует префиксу шаблона (в пределах расстояния редактирования k).Другими словами, Q_j - это максимальное начало соответствия k-правлению в конце рассмотренного ранее ввода.

С этим, вот псевдо-код алгоритма А.

Let r = root of (uncompressed) suffix trie 
Set r's cached d,l with formulas at end page 7 (0'th dp table columns) 
// Invariant: r contains cached d,l 
for each character t_j from input text T in sequence 
    Let s = g(r, t_j) // make the go-to transition from r on t_j 
    if visited(s) 
    r = s 
    while no cached d,l on node r 
     r = f(r) // traverse suffix edge 
    end while 
    else 
    Use cached d',l' on r to find new columns (d,l) = dp(d',l') 
    Compute |Q_j| = l[h], h = argmax(i).d[i]<=k as in the paper 
    r = s 
    while depth(r) != |Q_j| 
     mark r visited 
     r = f(r) // traverse suffix edge 
    end while 
    mark r visited 
    set cached d,l on node r 
    end if 
end for 

Я ушел из шаг вывода для простоты.

Что такое пересечение границ суффикса? Когда мы делаем это из узла r, где Key (r) = aX (ведущий a, за которым следует некоторая строка X), мы переходим к узлу с ключом X. Следствие: мы сохраняем каждый столбец, соответствующий жизнеспособному префиксу Q_h at trie node для суффикса ввода с префиксом Q_h. Функция f (s) = r является функцией перехода суффикса.

Для чего это стоит, Wikipedia picture of a suffix tree показывает это довольно хорошо. Например, если из узла для «NA» мы следуем за краем суффикса, мы переходим к узлу для «A», а оттуда к «». Мы всегда отключаем лидера. Поэтому, если мы будем обозначать состояние s Key (s), мы имеем f ("NA") = "A" и f ("A") = "". (Я не знаю, почему он не упоминает такие состояния в этой статье. Это упростит многие объяснения.)

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

Алгоритм B

Цель алгоритма B является идти быстрее, пропуская через вход, касаясь только те символы, вероятно, создать новый столбец, то есть те, которые являются концами ввода, которые соответствуют ранее невидимое жизнеспособными префикс шаблона.

Как вы подозреваете, ключом к алгоритму является функция loc. Грубо говоря, это скажет, где следующий «вероятный» входной символ. Алгоритм довольно похож на поиск A *. Мы сохраняем минимальную кучу (которая должна иметь операцию удаления), соответствующую набору S_i в документе. (Он называет это словарем, но это не очень традиционное использование этого термина.) В мини-куче содержатся потенциальные «следующие состояния», наложенные на позицию следующего «вероятного символа», как описано выше. Обработка одного символа создает новые записи. Мы продолжаем идти, пока куча не станет пустой.

Вы абсолютно правы, что здесь он становится отрывочным. Теоремы и леммы не связаны друг с другом, чтобы привести аргумент о правильности. Он предполагает, что вы переделаете его работу. Я не совсем уверен в этом размахивании руками. Но, похоже, этого достаточно, чтобы «декодировать» алгоритм, который он имеет в виду.

Другой основной концепцией является набор S_i и, в частности, подмножество, которое остается не устранено. Мы сохраним эти неубранные состояния в мини-куче H.

Вы правы, чтобы сказать, что обозначение затушевывает намерение S_i. Когда мы обрабатываем входные данные слева направо и занимаем позицию i, мы собрали множество реальных префиксов, которые были просмотрены до сих пор. Каждый раз, когда новый найден, вычисляется новый столбец dp. В авторских обозначениях эти префиксы были бы Q_h для всех h < = i или более формально {Q_h | h < = i}. Каждый из них имеет путь от корня до уникального узла. Множество S_i состоит из всех состояний, которые мы получаем, беря еще один шаг из всех этих узлов вдоль по краям в trie. Это дает тот же результат, что и весь текст, который ищет каждое появление Q_h и следующего символа a, а затем добавляет состояние, соответствующее Q_h a, в S_i, но это быстрее. Ключи для состояний S_i являются точными кандидатами на следующий жизнеспособный префикс Q_ {i + 1}.

Как выбрать подходящего кандидата? Выберите тот, который появляется после позиции i на входе. Здесь находится loc (ы). Значение loc для состояния s - это то, что я только что сказал выше: позиция на входе, начинающаяся с i, где следующий жизнеспособный префикс, связанный с этим состоянием.

Важным моментом является то, что мы не хотим просто назначать только что найденное (путем вытягивания значения min loc из H) «следующий» жизнеспособный префикс как Q_ {i + 1} (жизнеспособный префикс для столбца dp i +1) и перейти к следующему символу (i + 2). Здесь мы должны поставить сцену, чтобы как можно дальше проскользнуть до символа символа k (с dp-столбцом k), такого Q_k = Q_ {i + 1}. Мы проскальзываем вперед по краям суффикса, как в алгоритме A. Только на этот раз мы записываем наши шаги для будущего использования, изменяя H: удаление элементов, что является таким же, как удаление элементов из S_i, и изменение значений loc.

Определение функций loc (s) является открытым, и он никогда не говорит, как его вычислить. Также не упоминается, что loc (s) также является функцией i, обрабатываемая текущая позиция ввода (что он перескакивает из j в более ранних частях бумаги в i здесь для текущей позиции ввода бесполезно.) Воздействие заключается в том, что loc (ы) изменений при обработке ввода.

Оказалось, что часть определения, применимая к устраненным состояниям, «просто происходит», потому что состояния отмечены как исключенные после удаления формы H. Поэтому для этого случая нам нужно только проверить отметку.

В другом случае - неиспользуемые состояния - требуется, чтобы мы искали вперед на входе, ища следующее вхождение в тексте, который не покрыт какой-либо другой строкой. Это понятие покрытия - обеспечить, чтобы мы всегда имели дело только с самыми «длинными» возможными префиксами. Более короткие нужно игнорировать, чтобы избежать выдачи матчей, отличных от максимальных. Теперь поиск в прямом эфире звучит дорого, но, к счастью, у нас уже есть уже созданный суффикс, который позволяет нам делать это в O (| Key (s) |). Трой должен быть тщательно аннотирован, чтобы вернуть соответствующую входную позицию и избежать охваченных случаев Key (s), но это было бы не слишком сложно. Он никогда не упоминает, что делать, когда поиск терпит неудачу. Здесь loc (s) = \ infty, то есть он исключен и должен быть удален из H.

Возможно, самая прикольная часть алгоритма обновляет H, чтобы иметь дело с случаями, когда мы добавляем новое состояние s для жизнеспособного префикса, который охватывает Ключ (w) для некоторого w, который уже был в H. Это означает, что мы должны хирургически обновить элемент (loc (w) => w) в H. Оказывается, суффикс trie еще раз поддерживает это эффективно с его суффиксами.

Со всем этим в наших головах давайте попробуем для псевдокода.

H = { (0 => root) } // we use (loc => state) for min heap elements 
until H is empty 
    (j => s_j) = H.delete_min // remove the min loc mapping from 
    (d, l) = dp(d', l', j) where (d',l') are cached at paraent(s_j) 
    Compute |Q_j| = l[h], h = argmax(i).d[i]<=k as in the paper 
    r = s_j 
    while depth(r) > |Q_j| 
    mark r eliminated 
    H.delete (_ => r) // loc value doesn't matter 
    end while 
    set cached d,l on node r 

    // Add all the "next states" reachable from r by go-tos 
    for all s = g(r, a) for some character a 
    unless s.eliminated? 
     H.insert (loc(s) => s) // here is where we use the trie to find loc 

     // Update H elements that might be newly covered 
     w = f(s) // suffix transition 
     while w != null 
     unless w.eliminated? 
      H.increase_key(loc(w) => w) // using explanation in Lemma 9. 
      w = f(w) // suffix transition 
     end unless 
     end while 
    end unless 
    end for 
end until 

Снова я пропустил вывод для простоты. Я не скажу, что это правильно, но это в стадионе. Одна вещь состоит в том, что он упоминает, что мы должны обрабатывать Q_j для узлов не до «посещения», но я не понимаю, что означает «посещенный» в этом контексте. Я думаю, что посещаемые состояния по определению алгоритма А не произойдут, потому что они были удалены из Х. Это головоломка ...

Операция increase_key в лемме 9 спешно описана без каких-либо доказательств. Его утверждение о том, что операция min возможно в O (log | alphabet |) время, оставляет много для воображения.

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

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