Алгоритм быстрого k несоответствия строк


10

Я ищу быстрый алгоритм сопоставления строк k-несоответствие. Учитывая строку шаблона P длины m и текстовую строку T длины n, мне нужен быстрый (линейное время) алгоритм, чтобы найти все позиции, где P соответствует подстроке T с не более чем k несоответствиями. Это отличается от проблемы k-отличий (редактировать расстояние). Несовпадение подразумевает, что подстрока и шаблон имеют разные буквы не более чем в k позициях. Я действительно требую только k = 1 (не более 1 несоответствия), поэтому быстрого алгоритма для конкретного случая k = 1 также будет достаточно. Размер алфавита составляет 26 (без учета регистра текста на английском языке), поэтому требования к пространству не должны расти слишком быстро с размером алфавита (например, алгоритм FAAST, я полагаю, занимает пространство по экспоненте в размере алфавита и т. Д. подходит только для последовательностей белков и генов).

Подход, основанный на динамическом программировании, будет иметь тенденцию быть O (mn) в худшем случае, который будет слишком медленным. Я считаю, что для этого есть модификации алгоритма Бойера-Мура, но я не могу достать такие бумаги. У меня нет подписки на доступ к академическим журналам или публикациям, поэтому любые ссылки должны быть в открытом доступе.

Я был бы очень признателен за любые указатели или ссылки на свободно доступные документы, или сам алгоритм для этой проблемы.


2
Если шаблон является фиксированным (но текст для соответствия варьируется), вы могли бы потенциально создать конечный автомат и пропустить текст через него. Существуют также алгоритмы, использующие суффиксные деревья (обычно это хорошо, если текст постоянен и шаблон меняется, но также применим, если оба варианта различаются), вы можете найти некоторые ссылки в Интернете. (Пока не добавляю ответ, так как я не очень уверен в алгоритмах на основе дерева суффиксов, если кто-то знает, пожалуйста, не стесняйтесь игнорировать этот комментарий).
— Арьябхата

@Aryabhata Спасибо! И шаблон, и текст меняются. В этом контексте создание конечного автомата было бы слишком дорого, особенно если включить область для 1 несоответствия. Что касается суффиксных деревьев / массивов суффиксов, я никогда не использовал их и мало о них знаю, но у меня сложилось впечатление, что они медленны в построении и эффективны в основном для точного соответствия. Но я буду исследовать этот вариант дальше. Любые указатели в этом или любом другом направлении были бы наиболее полезны!
— Пареш

1
Нет, суффиксные деревья можно использовать и для приблизительных совпадений. По крайней мере, вики утверждают так: en.wikipedia.org/wiki/Suffix_tree
— Aryabhata

Ответы:


5

Для этой проблемы можно использовать суффиксные массивы . Они содержат начальные позиции каждого суффикса строки, отсортированного в лексикографическом порядке. Даже если они могут быть построены наивно в сложности , существуют методы, чтобы построить их в сложности Θ ( n ) . Смотрите, например, это и это . Давайте назовем этот суффиксный массив SA.O(nlog⁡n)Θ(n)

После создания массива суффиксов нам необходимо создать массив 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 ]uvu<vminu<=k<=v−1LCP[k], Хороший RMQ может предварительно обработать массив за O ( n ) или O ( n log n ) и ответить на запросы вида L C P [ u , v ] за O ( 1 ) . Смотрите здесь для краткого алгоритма RMQ, и здесь для хорошего учебника по RMQ и отношениям (и сокращениям) между LCA и RMQ. Это еще один хороший альтернативный подход.LCPO(n)O(nlog⁡n)LCP[u,v]O(1)

С помощью этой информации мы создаем массив суффиксов и связанные с ними массивы (как описано выше) для объединения двух строк с разделителем между ними (например, T # P, где «#» не встречается ни в одной из строк). Затем мы можем выполнить k несоответствие строк, используя метод "кенгуру". Это и это объясняет метод кенгуру в контексте деревьев суффиксов, но может быть непосредственно применен и к массивам суффиксов. Для каждого индекса текста T найдите L C P суффикса T, начинающегося с i, и суффикса PiTLCPTiPначиная с 0. Это дает местоположение, после которого возникает первое несоответствие при сопоставлении с T [ i ] . Пусть эта длина будет l 0 . Пропустите несовпадающий символ в T и P и попробуйте сопоставить оставшиеся строки. То есть снова найдите L C P из T [ i + l 0 + 1 ] и P [ l 0 + 1 ] . Повторяйте это, пока не получите k несоответствий или не завершится строка. каждыйPT[i]l0TPLCPT[i+l0+1]P[l0+1]k является O ( 1 ) . Есть O ( K ) L С Р «S для каждого индекса я из T , давая это общая сложность O ( п к ) .LCPO(1)O(k) LCPiTO(nk)

O(nk+(n+m)log⁡(n+m))O(nk+nlog⁡n)m=O(n)O(nk)


Большой! Теперь у меня есть кое-что в моем списке TODO :-)
— Aryabhata

Ссылка на siam.org во втором абзаце не работает, но ссылку на документ можно найти здесь epubs.siam.org/doi/pdf/10.1137/1.9781611972917.3
— leecbaker

4

O(n+m)kO(nk+m)

Идея аналогична алгоритму хэширования Рабина-Карпа для точных совпадений подстрок.

m2km/2k2k2k

k

k

Я ожидаю (предостережение: я не пробовал сам), вероятно, это будет быстрее на практике и, возможно, будет легче кодировать / поддерживать, чем использование подхода на основе суффиксного дерева.


Просто нужно пояснение. Под "... отделяйте каждую строку длиной m на 2k блоков размером m / 2k каждый ...", вы подразумеваете, что разделяете каждую подстроку длины m в T (длиной n) на 2k блоков. И этот хэш может быть вычислен в O (n) методом скользящего хэша. Затем строка шаблона также будет разделена на 2k блоков, и будут сравниваться соответствующие хэши, что дает возможность не более чем k блоков не совпадать. Если это так, то мы могли бы потенциально отбросить все случаи, когда количество несоответствий превышает k. Я правильно понял?
— Пареш

kΩ(nk)O(n)

Мне нравится этот подход! Тем не менее, этот подход в целом быстрый, но ухудшается до O (mnk), если количество совпадений велико (O (n) совпадений). Имея это в виду, я поддерживал два скользящих хэша, предполагая, что оба не могут иметь коллизию для одного и того же входа (я не делал этого математически, так как хотел видеть скорость). Таким образом, нам не нужно проверять совпадение char-by-char, если оба хэша совпадают. В целом это довольно быстро, но слишком много, если количество совпадений велико. С этим и с тем, как вы предложили, это было медленно для больших матчей.
— Пареш

mm/2kmO(nkm)

mm/2k2kk+1k+cΩ(nm)mm/2k
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.