Я ищу быстрый алгоритм сопоставления строк k-несоответствие. Учитывая строку шаблона P длины m и текстовую строку T длины n, мне нужен быстрый (линейное время) алгоритм, чтобы найти все позиции, где P соответствует подстроке T с не более чем k несоответствиями. Это отличается от проблемы k-отличий (редактировать расстояние). Несовпадение подразумевает, что подстрока и шаблон имеют разные буквы не более чем в k позициях. Я действительно требую только k = 1 (не более 1 несоответствия), поэтому быстрого алгоритма для конкретного случая k = 1 также будет достаточно. Размер алфавита составляет 26 (без учета регистра текста на английском языке), поэтому требования к пространству не должны расти слишком быстро с размером алфавита (например, алгоритм FAAST, я полагаю, занимает пространство по экспоненте в размере алфавита и т. Д. подходит только для последовательностей белков и генов).
Подход, основанный на динамическом программировании, будет иметь тенденцию быть O (mn) в худшем случае, который будет слишком медленным. Я считаю, что для этого есть модификации алгоритма Бойера-Мура, но я не могу достать такие бумаги. У меня нет подписки на доступ к академическим журналам или публикациям, поэтому любые ссылки должны быть в открытом доступе.
Я был бы очень признателен за любые указатели или ссылки на свободно доступные документы, или сам алгоритм для этой проблемы.
Ответы:
Для этой проблемы можно использовать суффиксные массивы . Они содержат начальные позиции каждого суффикса строки, отсортированного в лексикографическом порядке. Даже если они могут быть построены наивно в сложности , существуют методы, чтобы построить их в сложности Θ ( n ) . Смотрите, например, это и это . Давайте назовем этот суффиксный массив SA.O ( n logн ) Θ ( н )
После создания массива суффиксов нам необходимо создать массив Longest Common Prefix (LCP) для массива суффиксов. Массив LCP хранит длину самого длинного общего префикса между двумя последовательными префиксами в массиве суффиксов (лексикографические последовательные суффиксы). Таким образом, LCP [i] содержит длину самого длинного общего префикса между SA [i] и SA [i + 1]. Этот массив также может быть построен за линейное время: см. Здесь , здесь и здесь для некоторых хороших ссылок.
Теперь, чтобы вычислить длину самого длинного префикса, общего для любых двух суффиксов в дереве суффиксов (вместо последовательных суффиксов), нам нужно использовать некоторую структуру данных RMQ . В приведенных выше ссылках было показано (и это легко увидеть, если массив визуализировать как дерево суффиксов), что длина самого длинного общего префикса между двумя суффиксами, имеющими позиции и v ( u < v ) в массиве суффиксов , может быть получено как m i n u < = k < = v - 1 L C P [ k ]U v ты < v м я нu < = k < = v - 1L Cп[ к ] , Хороший RMQ может предварительно обработать массив за O ( n ) или O ( n log n ) и ответить на запросы вида L C P [ u , v ] за O ( 1 ) . Смотрите здесь для краткого алгоритма RMQ, и здесь для хорошего учебника по RMQ и отношениям (и сокращениям) между LCA и RMQ. Это еще один хороший альтернативный подход.L Cп O ( n ) O ( n logн ) L Cп[ u , v ] O ( 1 )
С помощью этой информации мы создаем массив суффиксов и связанные с ними массивы (как описано выше) для объединения двух строк с разделителем между ними (например, T # P, где «#» не встречается ни в одной из строк). Затем мы можем выполнить k несоответствие строк, используя метод "кенгуру". Это и это объясняет метод кенгуру в контексте деревьев суффиксов, но может быть непосредственно применен и к массивам суффиксов. Для каждого индекса текста T найдите L C P суффикса T, начинающегося с i, и суффикса Pi T LCP T i P начиная с 0. Это дает местоположение, после которого возникает первое несоответствие при сопоставлении с T [ i ] . Пусть эта длина будет l 0 . Пропустите несовпадающий символ в T и P и попробуйте сопоставить оставшиеся строки. То есть снова найдите L C P из T [ i + l 0 + 1 ] и P [ l 0 + 1 ] . Повторяйте это, пока не получите k несоответствий или не завершится строка. каждыйP T[i] l0 T P LCP T[i+l0+1] P[l0+1] k является O ( 1 ) . Есть O ( K ) L С Р «S для каждого индекса я из T , давая это общая сложность O ( п к ) .LCP O(1) O(k) LCP i T O(nk)
источник
Идея аналогична алгоритму хэширования Рабина-Карпа для точных совпадений подстрок.
Я ожидаю (предостережение: я не пробовал сам), вероятно, это будет быстрее на практике и, возможно, будет легче кодировать / поддерживать, чем использование подхода на основе суффиксного дерева.
источник