Алгоритм Ландау-Вишкина (k несовпадений) — различия между версиями
Margarita (обсуждение | вклад) м (→Построение pm) |
Margarita (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | '''Постановка задачи:''' | + | '''Постановка задачи:''' дано число <tex>k > 0</tex> текст <tex>y[1...n]</tex> и образец <tex>x[1...m]</tex>, <tex>m < n</tex>. Требуется найти все подстроки текста длины <tex>m</tex>, с не более чем <tex>k</tex> несовпадающими символами с образцом. Эту задачу решает алгоритм Ландау-Вишкина (k несовпадений) (''англ.Landau-Vishkin Algorithm'') |
− | =Алгоритм= | + | ==Алгоритм== |
− | ==Идея== | + | ===Идея=== |
− | [[Файл:algLandauVishkin1.png|thumb|250px|right| В таблицу tm по номеру несовпадения записывается соответстующий индекс образца, то есть <tex>tm[i][1] = a</tex>, <tex>tm[i][2] = b</tex>, <tex>tm[i][3] = c</tex> и т д.]] | + | [[Файл:algLandauVishkin1.png|thumb|250px|right| В таблицу <tex>tm</tex> по номеру несовпадения записывается соответстующий индекс образца, то есть <tex>tm[i][1] = a</tex>, <tex>tm[i][2] = b</tex>, <tex>tm[i][3] = c</tex> и т. д.]] |
При анализе используется двумерный массив несовпадений текста <tex>tm[0...n-m][1...k+1]</tex>, содержащий информацию о несовпадениях текста с образцом. По завершении анализа в его <tex>i</tex>-й строке содержатся позиции в <tex>x</tex> первых <tex>k+1</tex> несовпадений между строками <tex>x[1...m]</tex> и <tex>y[i+1...i+m]</tex>. Таким образом, если <tex>tm[i][v] = s</tex>, то <tex>y[i+s] \neq x[s]</tex>, и это <tex>v</tex>-е несовпадение между <tex>x[1...m]</tex> и <tex>y[i+1...i+m]</tex>, считая слева направо. Если число <tex>d</tex> несовпадений <tex>x[1...m]</tex> с подстрокой <tex>y[i+1...i+m]</tex> меньше <tex>k+1</tex>, то, начиная с <tex>d+1</tex>, элементы <tex>i</tex>-й строки равны значению по умолчанию <tex>m+1</tex>. | При анализе используется двумерный массив несовпадений текста <tex>tm[0...n-m][1...k+1]</tex>, содержащий информацию о несовпадениях текста с образцом. По завершении анализа в его <tex>i</tex>-й строке содержатся позиции в <tex>x</tex> первых <tex>k+1</tex> несовпадений между строками <tex>x[1...m]</tex> и <tex>y[i+1...i+m]</tex>. Таким образом, если <tex>tm[i][v] = s</tex>, то <tex>y[i+s] \neq x[s]</tex>, и это <tex>v</tex>-е несовпадение между <tex>x[1...m]</tex> и <tex>y[i+1...i+m]</tex>, считая слева направо. Если число <tex>d</tex> несовпадений <tex>x[1...m]</tex> с подстрокой <tex>y[i+1...i+m]</tex> меньше <tex>k+1</tex>, то, начиная с <tex>d+1</tex>, элементы <tex>i</tex>-й строки равны значению по умолчанию <tex>m+1</tex>. | ||
Строка 11: | Строка 11: | ||
Заметим, если <tex>tm[i][k+1] = m+1</tex>, то подстрока <tex>y[i+1...i+m]</tex> отличается от образца <tex>x</tex> не более, чем на <tex>k</tex> символов, и, таким образом, является решением задачи. | Заметим, если <tex>tm[i][k+1] = m+1</tex>, то подстрока <tex>y[i+1...i+m]</tex> отличается от образца <tex>x</tex> не более, чем на <tex>k</tex> символов, и, таким образом, является решением задачи. | ||
− | Затем образец сканируется параллельно с текстом слева на права по одному символу за раз. На итерации <tex>i</tex> с образцом сравнивается подстрока <tex>y[i+1...i+m]</tex>. <tex>j</tex> - обозначим самую правую позицию в тексте, достигнутую за предыдущие итерации, то есть <tex>j</tex> является максимальным из чисел <tex>r+tm[r][k + 1]</tex>, где <tex>0 \leqslant r < i</tex>. Если <tex>i < j</tex>, в <tex>b</tex> присваивается результат работы <tex>merge</tex>, которая находит количество несовпадений между <tex>x[1... j-i]</tex> и <tex>y[i+1...j]</tex>. Если <tex>b</tex> не превышает <tex>k</tex>, вызывается процедура <tex>extend</tex>, которая сравнивает подстроки <tex>y[j + 1...i + m]</tex> и <tex>x[j - i + 1...m]</tex>, где изменяется таблица текстовых несовпадений. Переменная <tex>r</tex> будет рассмотрена ниже. | + | Затем образец сканируется параллельно с текстом слева на права по одному символу за раз. На итерации <tex>i</tex> с образцом сравнивается подстрока <tex>y[i+1...i+m]</tex>. <tex>j</tex> - обозначим самую правую позицию в тексте, достигнутую за предыдущие итерации, то есть <tex>j</tex> является максимальным из чисел <tex>r+tm[r][k + 1]</tex>, где <tex>0 \leqslant r < i</tex>. Если <tex>i < j</tex>, в <tex>b</tex> присваивается результат работы <tex>merge</tex>, которая находит количество несовпадений между <tex>x[1...j-i]</tex> и <tex>y[i+1...j]</tex>. Если <tex>b</tex> не превышает <tex>k</tex>, вызывается процедура <tex>extend</tex>, которая сравнивает подстроки <tex>y[j + 1...i + m]</tex> и <tex>x[j - i + 1...m]</tex>, где изменяется таблица текстовых несовпадений. Переменная <tex>r</tex> будет рассмотрена ниже. |
{| border="0" | {| border="0" | ||
|align="left" colspan="4"| | |align="left" colspan="4"| | ||
<font size=2> | <font size=2> | ||
− | tm[0...n-m][1...k+1] = m+1 // инициализация | + | tm[0...n - m][1...k + 1] = m + 1 // инициализация |
r = 0 | r = 0 | ||
j = 0 | j = 0 | ||
Строка 29: | Строка 29: | ||
|} | |} | ||
− | ==Процедура extend== | + | ===Процедура extend=== |
[[Файл:algLandauVishkin2.png|thumb|380px|right| Синие подстроки сравниваются в процедуре extend. <tex>w < k + 1</tex>]] | [[Файл:algLandauVishkin2.png|thumb|380px|right| Синие подстроки сравниваются в процедуре extend. <tex>w < k + 1</tex>]] | ||
Строка 47: | Строка 47: | ||
|} | |} | ||
− | ==Процедура merge== | + | ===Процедура merge=== |
[[Файл:algLandauVishkin3.png|thumb|400px|right| Синие подстроки сравниваются в процедуре merge. Красным отмечены несовпадения.]] | [[Файл:algLandauVishkin3.png|thumb|400px|right| Синие подстроки сравниваются в процедуре merge. Красным отмечены несовпадения.]] | ||
− | Рассмотрим процедуру <tex>merge</tex> подробнее. Она находит количество несовпадений между <tex>x[1... j-i]</tex> и <tex>y[i+1...j]</tex> и устанавливает b равным найденному числу, при этом используется полученная ранее информация. Введем <tex>r</tex> - это строка таблицы несовпадений, в которой есть информация о несовпадениях, полученных при совмещении начала образца и <tex>y[r+1]</tex>. <tex>r+tm[r][k+1]</tex> содержит текущий номер самой правой из проверенных на настоящий момент позиций текста. Поэтому при обработки подстроки начинающейся с <tex>y[i+1]</tex>, можно учитывать информацию в <tex>r</tex>-ой строке <tex>tm</tex>, которая содержит информацию о сопоставлении образца с <tex>y[i]</tex>. Подходящими значениями из таблицы несовпадений являются, таким образом, <tex>tm[r][q ... k+1]</tex>, где <tex>q</tex> – это наименьшее из целых чисел, для которых <tex>r+tm[r][q] > i</tex>. Однако, следует учитывать тот факт, что эти несовпадения соответствуют началу образца, который был выровнен с <tex>y[r+1]</tex>, в то время как текущая позиция образца выровнена с <tex>y[i+1]</tex> – разница в <tex>i - r</tex> мест. | + | Рассмотрим процедуру <tex>merge</tex> подробнее. Она находит количество несовпадений между <tex>x[1... j-i]</tex> и <tex>y[i+1...j]</tex> и устанавливает <tex>b</tex> равным найденному числу, при этом используется полученная ранее информация. Введем <tex>r</tex> - это строка таблицы несовпадений, в которой есть информация о несовпадениях, полученных при совмещении начала образца и <tex>y[r+1]</tex>. <tex>r+tm[r][k+1]</tex> содержит текущий номер самой правой из проверенных на настоящий момент позиций текста. Поэтому при обработки подстроки начинающейся с <tex>y[i+1]</tex>, можно учитывать информацию в <tex>r</tex>-ой строке <tex>tm</tex>, которая содержит информацию о сопоставлении образца с <tex>y[i]</tex>. Подходящими значениями из таблицы несовпадений являются, таким образом, <tex>tm[r][q ... k+1]</tex>, где <tex>q</tex> – это наименьшее из целых чисел, для которых <tex>r+tm[r][q] > i</tex>. Однако, следует учитывать тот факт, что эти несовпадения соответствуют началу образца, который был выровнен с <tex>y[r+1]</tex>, в то время как текущая позиция образца выровнена с <tex>y[i+1]</tex> – разница в <tex>i - r</tex> мест. |
[[Файл:algLandauVishkin4.png|thumb|250px|right| В таблицу pm по номеру несовпадения записывается соответствующий индекс верхнего образца, то есть <tex>pm[i][1] = a</tex>, <tex>pm[i][2] = b</tex>, <tex>pm[i][3] = c</tex> и т д.]] | [[Файл:algLandauVishkin4.png|thumb|250px|right| В таблицу pm по номеру несовпадения записывается соответствующий индекс верхнего образца, то есть <tex>pm[i][1] = a</tex>, <tex>pm[i][2] = b</tex>, <tex>pm[i][3] = c</tex> и т д.]] | ||
− | Также в алгоритме используется двумерный массив несовпадений образца <tex>pm[1...m-1][1...2k+1]</tex>, генерируемой на стадии предварительной обработки образца. В нем содержатся позиции несовпадения образца с самим собой при различных сдвигах, аналогично <tex>tm</tex>, то есть в <tex>i</tex>-ой строке содержатся позиции внутри <tex>x</tex> первых <tex>2k+1</tex> несовпадений между подстроками <tex>x[1...m-i]</tex> и <tex>x[i+1...m]</tex>. Таким образом, если <tex>pm[i][v] = s</tex>, то <tex>x[i+s] \neq x[s]</tex>, и это <tex>v</tex>-е несовпадение между <tex>x | + | Также в алгоритме используется двумерный массив несовпадений образца <tex>pm[1...m-1][1...2k+1]</tex>, генерируемой на стадии предварительной обработки образца. В нем содержатся позиции несовпадения образца с самим собой при различных сдвигах, аналогично <tex>tm</tex>, то есть в <tex>i</tex>-ой строке содержатся позиции внутри <tex>x</tex> первых <tex>2k+1</tex> несовпадений между подстроками <tex>x[1...m-i]</tex> и <tex>x[i+1...m]</tex>. Таким образом, если <tex>pm[i][v] = s</tex>, то <tex>x[i+s] \neq x[s]</tex>, и это <tex>v</tex>-е несовпадение между <tex>x[1...m-i]</tex> и <tex>x[i+1...m]</tex> слева направо. Если число <tex>d</tex> несовпадений между этими строками меньше <tex>2k+1</tex>, то, начиная с <tex>d+1</tex>, элементы <tex>i</tex>-й строки равны <tex>m+1</tex>, значению по умолчанию. Построение <tex>tm</tex> будет подробнее рассмотрено позднее. |
Таким образом, для <tex>merge</tex> интерес представляет строка <tex>i - r</tex> таблицы несовпадений образца, причем используются значения <tex>pm[i-r][1...t]</tex>, где <tex>t</tex> – самое правое несовпадение в <tex>pm[i-r][1...2k+1]</tex>, такое, что <tex>pm[i-r][t] < j-i+1</tex>, так как требуются только несовпадения в подстроке <tex>x[1...j-i]</tex>. | Таким образом, для <tex>merge</tex> интерес представляет строка <tex>i - r</tex> таблицы несовпадений образца, причем используются значения <tex>pm[i-r][1...t]</tex>, где <tex>t</tex> – самое правое несовпадение в <tex>pm[i-r][1...2k+1]</tex>, такое, что <tex>pm[i-r][t] < j-i+1</tex>, так как требуются только несовпадения в подстроке <tex>x[1...j-i]</tex>. | ||
Строка 65: | Строка 65: | ||
[[Файл:algLandauVishkin5.png|380px]] | [[Файл:algLandauVishkin5.png|380px]] | ||
− | '''Условие B''': для двух копий образца, со сдвигом относительно друг друга <tex>i - r</tex>, совмещенных с текстом так, что их начальные символы лежат, соответственно, над <tex>y[r+1]</tex> и <tex>y[i+1]</tex>, позиция <tex>p</tex> соответствует несовпадению между двумя образцам, то есть <tex>x[p-r] \neq x[p-i]</tex>. Это <tex>u</tex>-е несовпадение при этом сдвиге, где <tex>1 < u < t</tex>, то есть <tex>p-i | + | '''Условие B''': для двух копий образца, со сдвигом относительно друг друга <tex>i - r</tex>, совмещенных с текстом так, что их начальные символы лежат, соответственно, над <tex>y[r+1]</tex> и <tex>y[i+1]</tex>, позиция <tex>p</tex> соответствует несовпадению между двумя образцам, то есть <tex>x[p-r] \neq x[p-i]</tex>. Это <tex>u</tex>-е несовпадение при этом сдвиге, где <tex>1 < u < t</tex>, то есть <tex>p-i = pm[i-r][u]</tex>. |
[[Файл:algLandauVishkin6.png|450px]] | [[Файл:algLandauVishkin6.png|450px]] | ||
Строка 73: | Строка 73: | ||
'''Случай 1: !A and !B:''' То есть, <tex>y[p] = x[p-r]</tex> и <tex>x[p-r] = x[p-i]</tex>, откуда <tex>y[p] = x[p-i]</tex>. Нет необходимости сравнивать символ текста с символом образца, так как ясно, что в этой позиции они совпадают. | '''Случай 1: !A and !B:''' То есть, <tex>y[p] = x[p-r]</tex> и <tex>x[p-r] = x[p-i]</tex>, откуда <tex>y[p] = x[p-i]</tex>. Нет необходимости сравнивать символ текста с символом образца, так как ясно, что в этой позиции они совпадают. | ||
− | '''Случай 2: (A and !B) or (!A and B):''' В любом случае <tex>y[p] \neq x[p-i]</tex> (если лишь условие | + | '''Случай 2: (A and !B) or (!A and B):''' В любом случае <tex>y[p] \neq x[p-i]</tex> (если лишь ''условие A'' истинно, то <tex>y[p] \neq x[p-r]</tex> и <tex>x[p-r] = x[p-i]</tex>, откуда <tex>y[p] \neq x[p-i]</tex>, с другой стороны, если выполнено только ''условие B'', то <tex>y[p] = x[p-r]</tex> и <tex>x[p-r] \neq x[p-i]</tex>, и опять, <tex>y[p] \neq x[p-i]</tex>). Как и в предыдущем случае, нет необходимости сравнивать символ текста с символом образца, так как известно, что они не совпадают. |
'''Случай 3: A and B:''' В этом случае мы ничего не можем сказать о том, совпадают ли символы <tex>y[p]</tex> и <tex>x[p-i]</tex>, поэтому их надо сравнить. | '''Случай 3: A and B:''' В этом случае мы ничего не можем сказать о том, совпадают ли символы <tex>y[p]</tex> и <tex>x[p-i]</tex>, поэтому их надо сравнить. | ||
− | Возвращаемся к процедуре merge. В случае 2, или если в случае 3 выявлено несовпадение символов, необходимо увеличить количество несовпадений символов <tex>b</tex> на единицу и обновить <tex>tm[i][b]</tex>. Соответствующими значениями таблицы для <tex>merge</tex> являются <tex>tm[i-r][1...t]</tex> и <tex>tm[r][q...k+1]</tex>. Переменные <tex>u</tex> и <tex>v</tex> в начале устанавливаются равными индексам первых элементов этих двух массивов, соответственно, и последовательно увеличиваются. | + | Возвращаемся к процедуре <tex>merge</tex>. В ''случае 2'', или если в ''случае 3'' выявлено несовпадение символов, необходимо увеличить количество несовпадений символов <tex>b</tex> на единицу и обновить <tex>tm[i][b]</tex>. Соответствующими значениями таблицы для <tex>merge</tex> являются <tex>tm[i-r][1...t]</tex> и <tex>tm[r][q...k+1]</tex>. Переменные <tex>u</tex> и <tex>v</tex> в начале устанавливаются равными индексам первых элементов этих двух массивов, соответственно, и последовательно увеличиваются. |
Условия окончания работы процедуры следующие: | Условия окончания работы процедуры следующие: | ||
Строка 87: | Строка 87: | ||
* Процедуру можно прервать, если <tex>i+pm[i-r][u] > j</tex> и <tex>tm[r][v] = m+1</tex>. Если выполняется вторая часть этого условия, то <tex>r+tm[r][v]</tex> равняется <tex>j</tex>, и соответствует суммам для последующих значений <tex>v</tex> вплоть до <tex>k+1</tex>. В этом случае процедура может быть прервана, если выполняется также первая часть приведенного условия, так как она указывает, что позиция текста <tex>j</tex> фактически пропущена. | * Процедуру можно прервать, если <tex>i+pm[i-r][u] > j</tex> и <tex>tm[r][v] = m+1</tex>. Если выполняется вторая часть этого условия, то <tex>r+tm[r][v]</tex> равняется <tex>j</tex>, и соответствует суммам для последующих значений <tex>v</tex> вплоть до <tex>k+1</tex>. В этом случае процедура может быть прервана, если выполняется также первая часть приведенного условия, так как она указывает, что позиция текста <tex>j</tex> фактически пропущена. | ||
− | Остается показать, что число позиций несовпадений в таблице несовпадений образца достаточно для того, чтобы <tex>merge</tex> нашла все, или, если их больше <tex>k+1</tex>, первые <tex>k+1</tex> несовпадений для <tex>y[i+1...j]</tex>. Это можно показать следующим образом. Условие A выполняется не больше чем для <tex>k+1</tex> позиции текста в диапазоне <tex>y[i+1...j]</tex>. Условие B выполняется для некоторого неизвестного числа позиций в этом же интервале. Строка <tex>i-r</tex> в таблице несовпадений образца, <tex>tm[i-r][1...2k+1]</tex>, содержит не больше чем <tex>2k+1</tex> позиций несовпадений между двумя копиями образца, с соответствующим сдвигом <tex>i-r</tex>. Если <tex>pm[i-r][2k+1] > j - i</tex>, то таблица содержит все позиции несовпадения образца самим с собой, у которых условие B выполняется для позиций текста в интервале <tex>y[i+1...j]</tex>. С другой стороны, если <tex>pm[i-r][2k+1] < j-i</tex>, то таблица может дать <tex>2k+1</tex> позиций текста в диапазоне <tex>y[i+1...j-1]</tex>, для которых выполняется условие B. Поскольку <tex>j = r+tm[r][k+1]</tex>, в диапазоне <tex>y[i+1...j-1]</tex> имеется до <tex>k</tex> позиций текста, для которых выполняется условие A. Таким образом, в худшем случае может быть <tex>k</tex> позиций, для которых имеет место случай 3, и которые требуется сравнить напрямую. Остается по крайней мере <tex>k+1</tex> позиций, удовлетворяющих условию B, но не условию A (случай 2), что является достаточным, чтобы заключить, что для данного положения образца относительно текста имеется не меньше <tex>k+1</tex> несовпадений между текстом и образцом. | + | Остается показать, что число позиций несовпадений в таблице несовпадений образца достаточно для того, чтобы <tex>merge</tex> нашла все, или, если их больше <tex>k+1</tex>, первые <tex>k+1</tex> несовпадений для <tex>y[i+1...j]</tex>. Это можно показать следующим образом. ''Условие A'' выполняется не больше чем для <tex>k+1</tex> позиции текста в диапазоне <tex>y[i+1...j]</tex>. ''Условие B'' выполняется для некоторого неизвестного числа позиций в этом же интервале. Строка <tex>i-r</tex> в таблице несовпадений образца, <tex>tm[i-r][1...2k+1]</tex>, содержит не больше чем <tex>2k+1</tex> позиций несовпадений между двумя копиями образца, с соответствующим сдвигом <tex>i-r</tex>. Если <tex>pm[i-r][2k+1] > j - i</tex>, то таблица содержит все позиции несовпадения образца самим с собой, у которых ''условие B'' выполняется для позиций текста в интервале <tex>y[i+1...j]</tex>. С другой стороны, если <tex>pm[i-r][2k+1] < j-i</tex>, то таблица может дать <tex>2k+1</tex> позиций текста в диапазоне <tex>y[i+1...j-1]</tex>, для которых выполняется ''условие B''. Поскольку <tex>j = r+tm[r][k+1]</tex>, в диапазоне <tex>y[i+1...j-1]</tex> имеется до <tex>k</tex> позиций текста, для которых выполняется ''условие A''. Таким образом, в худшем случае может быть <tex>k</tex> позиций, для которых имеет место ''случай 3'', и которые требуется сравнить напрямую. Остается по крайней мере <tex>k+1</tex> позиций, удовлетворяющих ''условию B'', но не ''условию A'' (''случай 2''), что является достаточным, чтобы заключить, что для данного положения образца относительно текста имеется не меньше <tex>k+1</tex> несовпадений между текстом и образцом. |
{| border="0" | {| border="0" | ||
Строка 105: | Строка 105: | ||
u++ | u++ | ||
else if i + pm[i - r][u] = r + tm[r][v] // Случай 3 | else if i + pm[i - r][u] = r + tm[r][v] // Случай 3 | ||
− | if x[ pm[i-r][u] ] != y[ i+pm[i-r][u] ] | + | if x[pm[i - r][u]] != y[i + pm[i - r][u]] |
b++ | b++ | ||
tm[i][b] = pm[i - r][u] | tm[i][b] = pm[i - r][u] | ||
Строка 113: | Строка 113: | ||
|} | |} | ||
− | ==Построение pm== | + | ===Построение pm=== |
Теперь осталось только обратиться к вычислению таблицы несовпадений образца на стадии предварительных вычислений. Не теряя общности, можно предположить, что <tex>m</tex> является некоторой степенью <tex>2</tex>. В алгоритме предварительной обработки используется разбиение множества <tex>{1, 2, ... , m-1}</tex> из <tex>m-1</tex> строк <tex>pm</tex> на следующие <tex>\log m</tex> подмножеств: | Теперь осталось только обратиться к вычислению таблицы несовпадений образца на стадии предварительных вычислений. Не теряя общности, можно предположить, что <tex>m</tex> является некоторой степенью <tex>2</tex>. В алгоритме предварительной обработки используется разбиение множества <tex>{1, 2, ... , m-1}</tex> из <tex>m-1</tex> строк <tex>pm</tex> на следующие <tex>\log m</tex> подмножеств: | ||
Строка 121: | Строка 121: | ||
Алгоритм состоит из <tex>\log m</tex> этапов. На этапе <tex>s</tex>, где <tex>1 \leqslant s < \log m</tex>, вычисляются строки <tex>pm</tex> в множестве <tex>s</tex>, где множество <tex>s</tex> {{---}} это <tex>\{2{s-1}, ... , 2^{s}-1\}</tex>. | Алгоритм состоит из <tex>\log m</tex> этапов. На этапе <tex>s</tex>, где <tex>1 \leqslant s < \log m</tex>, вычисляются строки <tex>pm</tex> в множестве <tex>s</tex>, где множество <tex>s</tex> {{---}} это <tex>\{2{s-1}, ... , 2^{s}-1\}</tex>. | ||
− | Метод, используемый для вычисления этой таблицы, основан на методе, используемом на стадии анализа текста. Рассмотрим алгоритм для этапа <tex>s</tex>. На стадии <tex>s</tex> входами для алгоритма анализа образца являются подстроки образца <tex>x[1...m-2^{s-1}]</tex> и <tex>x[2^{s-1}+1...m]</tex>, которые трактуются здесь, соответственно, как образец и текст, и массив <tex>pm[1...2^{s-1}-1][1...min\{2^{log(m)-s}4k+1, m-2^{s-1}\}]</tex>, содержащий выходы предыдущих <tex>s - 1</tex> стадий. Выходы стадии <tex>s</tex> вводятся в pm. За исключением стадии <tex>\log m</tex>, на которой находят до <tex>2k+1</tex> несовпадений, на стадии <tex>s</tex> для каждой строки <tex>pm</tex> требуется найти до <tex>min\{2^{log(m)-s}2k+1, m-2^{s}\}</tex> несовпадений, а не до <tex>k+1</tex>, как в алгоритме анализа текста. | + | Метод, используемый для вычисления этой таблицы, основан на методе, используемом на стадии анализа текста. Рассмотрим алгоритм для этапа <tex>s</tex>. На стадии <tex>s</tex> входами для алгоритма анализа образца являются подстроки образца <tex>x[1...m-2^{s-1}]</tex> и <tex>x[2^{s-1}+1...m]</tex>, которые трактуются здесь, соответственно, как образец и текст, и массив <tex>pm[1...2^{s-1}-1][1...\min\{2^{log(m)-s}4k+1, m-2^{s-1}\}]</tex>, содержащий выходы предыдущих <tex>s - 1</tex> стадий. Выходы стадии <tex>s</tex> вводятся в <tex>pm</tex>. За исключением стадии <tex>\log m</tex>, на которой находят до <tex>2k+1</tex> несовпадений, на стадии <tex>s</tex> для каждой строки <tex>pm</tex> требуется найти до <tex>\min\{2^{log(m)-s}2k+1, m-2^{s}\}</tex> несовпадений, а не до <tex>k+1</tex>, как в алгоритме анализа текста. |
{| border="0" | {| border="0" | ||
|align="left" colspan="4"| | |align="left" colspan="4"| | ||
<font size=2> | <font size=2> | ||
− | pm[<tex>2^{s-1}</tex>...<tex>2^{s}-1</tex>][1...min{<tex>2^{\log (m-1)}2k-1</tex>, <tex>m-2^{s}</tex>}] = m+1 | + | pm[<tex>2^{s-1}</tex>...<tex>2^{s} - 1</tex>][1...min{<tex>2^{\log (m-1)}2k - 1</tex>, <tex>m - 2^{s}</tex>}] = m + 1 |
r = <tex>2^{s-1}</tex> | r = <tex>2^{s-1}</tex> | ||
j = <tex>2^{s-1}</tex> | j = <tex>2^{s-1}</tex> | ||
Строка 133: | Строка 133: | ||
if i < j | if i < j | ||
merge(i, r, j, b) | merge(i, r, j, b) | ||
− | if b < min{<tex>2^{log(m-1)}2k-1, m-2^{s} </tex>} | + | if b < min{<tex>2^{log(m-1)}2k - 1, m - 2^{s} </tex>} |
r = i | r = i | ||
extend(i, j, b) | extend(i, j, b) | ||
Строка 139: | Строка 139: | ||
|} | |} | ||
− | ==Оценка работы== | + | ===Оценка работы=== |
Теперь исследуем затраты времени на анализ текста. Если исключить вызовы процедур <tex>merge</tex> и <tex>extend</tex>, каждая из <tex>n-m+1</tex> итераций цикла анализа текста выполняется за фиксированное время, что дает в общей сложности время <tex>O(n)</tex>. Общее число операций, выполняемых процедурой <tex>extend</tex> во время вызовов равно <tex>O(n)</tex>, так как она проверяет каждый символ текста не больше одного раза. Процедура <tex>merge</tex> при каждом вызове обрабатывает массив <tex>pm[i-r][1...2k+1]</tex> и <tex>tm[r][1...k+1]</tex>, которые в сумме имеют <tex>3k+2</tex> элементов. Время работы <tex>merge</tex> можно рассчитать, соотнеся операции с фиксированным временем с каждым из этих входов, что дает время счета для каждого вызова, равное <tex>O(k)</tex>. Таким образом, можно видеть, что общее время анализа текста составляет <tex>O(nk)</tex>. | Теперь исследуем затраты времени на анализ текста. Если исключить вызовы процедур <tex>merge</tex> и <tex>extend</tex>, каждая из <tex>n-m+1</tex> итераций цикла анализа текста выполняется за фиксированное время, что дает в общей сложности время <tex>O(n)</tex>. Общее число операций, выполняемых процедурой <tex>extend</tex> во время вызовов равно <tex>O(n)</tex>, так как она проверяет каждый символ текста не больше одного раза. Процедура <tex>merge</tex> при каждом вызове обрабатывает массив <tex>pm[i-r][1...2k+1]</tex> и <tex>tm[r][1...k+1]</tex>, которые в сумме имеют <tex>3k+2</tex> элементов. Время работы <tex>merge</tex> можно рассчитать, соотнеся операции с фиксированным временем с каждым из этих входов, что дает время счета для каждого вызова, равное <tex>O(k)</tex>. Таким образом, можно видеть, что общее время анализа текста составляет <tex>O(nk)</tex>. | ||
− | Рассмотрим построение <tex>pm</tex>. Используя аргументы, аналогичные применявшимся при проверке корректности процедуры merge, можно показать, что для нахождения требуемого количества несовпадений на стадии s требуется <tex>min\{2^{log(m)-s}4k+1, m-2^{s}\}</tex> позиций, для которых выполняется условие B, и в особом случае, а именно, на стадии <tex>\log m</tex>, требуется <tex>4k + 1</tex> таких позиций. | + | Рассмотрим построение <tex>pm</tex>. Используя аргументы, аналогичные применявшимся при проверке корректности процедуры merge, можно показать, что для нахождения требуемого количества несовпадений на стадии s требуется <tex>\min\{2^{log(m)-s}4k+1, m-2^{s}\}</tex> позиций, для которых выполняется условие B, и в особом случае, а именно, на стадии <tex>\log m</tex>, требуется <tex>4k + 1</tex> таких позиций. |
− | На каждой стадии <tex>s</tex> из <tex>\log m</tex> стадий анализа образца цикл <tex>for</tex> производит <tex>2^{s-1}</tex> итераций <tex>(2^{s-1} \leqslant i \leqslant 2^{s}-1)</tex>. Если не считать время работы процедур <tex>merge</tex> и <tex>extend</tex>, каждая итерация требует фиксированного времени. Для всех итераций на шаге <tex>s</tex> процедуре <tex>extend</tex> требуется время <tex>O(m)</tex>. Ранее было показано, что время работы <tex>merge</tex> пропорционально числу искомых несовпадений. Таким образом, каждый вызов <tex>merge</tex> занимает время <tex>O(min\{2^{log(m)-s}4k+1, m-2^{s}\})</tex>, что равно <tex>O(2k2^{\log (m)-s})</tex>. Таким образом, общее время для стадии <tex>s</tex> равно <tex>O(m+2^{s-1}(2k2^{\log (m) -s}))</tex> = <tex>O(km)</tex>. Проведя суммирование по всем стадиям, получаем общее время счета <tex>O(\sum_{i=1}^{\log m} km) | + | На каждой стадии <tex>s</tex> из <tex>\log m</tex> стадий анализа образца цикл <tex>for</tex> производит <tex>2^{s-1}</tex> итераций <tex>(2^{s-1} \leqslant i \leqslant 2^{s}-1)</tex>. Если не считать время работы процедур <tex>merge</tex> и <tex>extend</tex>, каждая итерация требует фиксированного времени. Для всех итераций на шаге <tex>s</tex> процедуре <tex>extend</tex> требуется время <tex>O(m)</tex>. Ранее было показано, что время работы <tex>merge</tex> пропорционально числу искомых несовпадений. Таким образом, каждый вызов <tex>merge</tex> занимает время <tex>O(\min\{2^{log(m)-s}4k+1, m-2^{s}\})</tex>, что равно <tex>O(2k2^{\log (m)-s})</tex>. Таким образом, общее время для стадии <tex>s</tex> равно <tex>O(m+2^{s-1}(2k2^{\log (m) -s}))</tex> = <tex>O(km)</tex>. Проведя суммирование по всем стадиям, получаем общее время счета <tex>O</tex> <tex>\displaystyle (\sum_{i=1}^{\log m} km) = O(dkm \log m)</tex>. Таким образом, общие затраты времени, включающие предварительную обработку образца и анализ текста, равны <tex>O(k(n + m \log m))</tex>. |
− | =Пример= | + | ==Пример== |
Пусть <tex>x = "tram"</tex>, <tex>y = "thetrippedtrap"</tex>, <tex>k = 2</tex>. | Пусть <tex>x = "tram"</tex>, <tex>y = "thetrippedtrap"</tex>, <tex>k = 2</tex>. | ||
{| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse; text-align: center;" | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse; text-align: center;" |
Версия 20:58, 16 июня 2014
Постановка задачи: дано число
текст и образец , . Требуется найти все подстроки текста длины , с не более чем несовпадающими символами с образцом. Эту задачу решает алгоритм Ландау-Вишкина (k несовпадений) (англ.Landau-Vishkin Algorithm)Содержание
Алгоритм
Идея
При анализе используется двумерный массив несовпадений текста
, содержащий информацию о несовпадениях текста с образцом. По завершении анализа в его -й строке содержатся позиции в первых несовпадений между строками и . Таким образом, если , то , и это -е несовпадение между и , считая слева направо. Если число несовпадений с подстрокой меньше , то, начиная с , элементы -й строки равны значению по умолчанию .Заметим, если
, то подстрока отличается от образца не более, чем на символов, и, таким образом, является решением задачи.Затем образец сканируется параллельно с текстом слева на права по одному символу за раз. На итерации
с образцом сравнивается подстрока . - обозначим самую правую позицию в тексте, достигнутую за предыдущие итерации, то есть является максимальным из чисел , где . Если , в присваивается результат работы , которая находит количество несовпадений между и . Если не превышает , вызывается процедура , которая сравнивает подстроки и , где изменяется таблица текстовых несовпадений. Переменная будет рассмотрена ниже.
tm[0...n - m][1...k + 1] = m + 1 // инициализация r = 0 j = 0 for i = 0 to n - m b = 0 if i < j b = merge(i, r, j) if b < k + 1 r = i extend(i, j, b)
|
Процедура extend
Рассмотрим процедуру
подробнее. Она сравнивает подстроки и , в случае несовпадения увеличивается и таблица текстовых несовпадений обновляется. Это происходит пока либо не будет найдено несовпадений (учитывая несовпадения, которые были найдены раньше на -ой итерации), либо не будет достигнуто с не больше чем несовпадениями, то есть найдено вхождение образца, начинающееся с .
extend(i, j, b) while (b < k + 1) and (j - i < m) j++ if y[j] != x[j-1] b++ tm[i][b] = j - i
|
Процедура merge
Рассмотрим процедуру
подробнее. Она находит количество несовпадений между и и устанавливает равным найденному числу, при этом используется полученная ранее информация. Введем - это строка таблицы несовпадений, в которой есть информация о несовпадениях, полученных при совмещении начала образца и . содержит текущий номер самой правой из проверенных на настоящий момент позиций текста. Поэтому при обработки подстроки начинающейся с , можно учитывать информацию в -ой строке , которая содержит информацию о сопоставлении образца с . Подходящими значениями из таблицы несовпадений являются, таким образом, , где – это наименьшее из целых чисел, для которых . Однако, следует учитывать тот факт, что эти несовпадения соответствуют началу образца, который был выровнен с , в то время как текущая позиция образца выровнена с – разница в мест.Также в алгоритме используется двумерный массив несовпадений образца
, генерируемой на стадии предварительной обработки образца. В нем содержатся позиции несовпадения образца с самим собой при различных сдвигах, аналогично , то есть в -ой строке содержатся позиции внутри первых несовпадений между подстроками и . Таким образом, если , то , и это -е несовпадение между и слева направо. Если число несовпадений между этими строками меньше , то, начиная с , элементы -й строки равны , значению по умолчанию. Построение будет подробнее рассмотрено позднее.Таким образом, для
интерес представляет строка таблицы несовпадений образца, причем используются значения , где – самое правое несовпадение в , такое, что , так как требуются только несовпадения в подстроке .Чтобы использовать упомянутую информацию в процедуре
, рассмотрим в тексте позицию , находящуюся в диапазоне, . Рассмотрим следующие условия для позиции :Условие A: когда символы
и совмещены, позиция в тексте соответствует предварительно выявленному несовпадению между образцом и текстом, то есть , и это несовпадение номер , где , то есть .Условие B: для двух копий образца, со сдвигом относительно друг друга
, совмещенных с текстом так, что их начальные символы лежат, соответственно, над и , позиция соответствует несовпадению между двумя образцам, то есть . Это -е несовпадение при этом сдвиге, где , то есть .Вспомним, что нас интересует, совпадает ли символ текста в позиции
с соответствующими символом образца, когда совмещен с , то есть верно ли, что . Рассмотрим этот вопрос при разных комбинациях указанных выше условий.Случай 1: !A and !B: То есть,
и , откуда . Нет необходимости сравнивать символ текста с символом образца, так как ясно, что в этой позиции они совпадают.Случай 2: (A and !B) or (!A and B): В любом случае
(если лишь условие A истинно, то и , откуда , с другой стороны, если выполнено только условие B, то и , и опять, ). Как и в предыдущем случае, нет необходимости сравнивать символ текста с символом образца, так как известно, что они не совпадают.Случай 3: A and B: В этом случае мы ничего не можем сказать о том, совпадают ли символы
и , поэтому их надо сравнить.Возвращаемся к процедуре
. В случае 2, или если в случае 3 выявлено несовпадение символов, необходимо увеличить количество несовпадений символов на единицу и обновить . Соответствующими значениями таблицы для являются и . Переменные и в начале устанавливаются равными индексам первых элементов этих двух массивов, соответственно, и последовательно увеличиваются.Условия окончания работы процедуры следующие:
- Если , то для случая, когда образец расположен относительно текста так, что совмещен с , обнаружено несовпадение, поэтому из процедуры можно выйти.
- Bспомним, что самая правая из интересующих нас позиций в , а именно, , равна , если , поэтому будет уже использовано для предыдущего значения , а именно, , и поэтому позиция должна быть пропущена. Следовательно, в этом случае также можно выйти из процедуры.
- Процедуру можно прервать, если и . Если выполняется вторая часть этого условия, то равняется , и соответствует суммам для последующих значений вплоть до . В этом случае процедура может быть прервана, если выполняется также первая часть приведенного условия, так как она указывает, что позиция текста фактически пропущена.
Остается показать, что число позиций несовпадений в таблице несовпадений образца достаточно для того, чтобы
нашла все, или, если их больше , первые несовпадений для . Это можно показать следующим образом. Условие A выполняется не больше чем для позиции текста в диапазоне . Условие B выполняется для некоторого неизвестного числа позиций в этом же интервале. Строка в таблице несовпадений образца, , содержит не больше чем позиций несовпадений между двумя копиями образца, с соответствующим сдвигом . Если , то таблица содержит все позиции несовпадения образца самим с собой, у которых условие B выполняется для позиций текста в интервале . С другой стороны, если , то таблица может дать позиций текста в диапазоне , для которых выполняется условие B. Поскольку , в диапазоне имеется до позиций текста, для которых выполняется условие A. Таким образом, в худшем случае может быть позиций, для которых имеет место случай 3, и которые требуется сравнить напрямую. Остается по крайней мере позиций, удовлетворяющих условию B, но не условию A (случай 2), что является достаточным, чтобы заключить, что для данного положения образца относительно текста имеется не меньше несовпадений между текстом и образцом.
merge(i, r, j, b) u = 1 v = q while (b < k + 1) and (v < k + 2) and (i + pm[i - r][u] < j or tm[r][v] != m + 1) if i + pm[i - r][u] > r + tm[r][v] // Случай 2, условие A b++ tm[i][b] = tm[r][v] - (i - r) v++ else if i + pm[i - r][u] < r + tm[r][v] // Случай 2, условие B b++ tm[i][b] = pm[i - r][u] u++ else if i + pm[i - r][u] = r + tm[r][v] // Случай 3 if x[pm[i - r][u]] != y[i + pm[i - r][u]] b++ tm[i][b] = pm[i - r][u] u++ v++
|
Построение pm
Теперь осталось только обратиться к вычислению таблицы несовпадений образца на стадии предварительных вычислений. Не теряя общности, можно предположить, что
является некоторой степенью . В алгоритме предварительной обработки используется разбиение множества из строк на следующие подмножеств:
Алгоритм состоит из
этапов. На этапе , где , вычисляются строки в множестве , где множество — это .Метод, используемый для вычисления этой таблицы, основан на методе, используемом на стадии анализа текста. Рассмотрим алгоритм для этапа
. На стадии входами для алгоритма анализа образца являются подстроки образца и , которые трактуются здесь, соответственно, как образец и текст, и массив , содержащий выходы предыдущих стадий. Выходы стадии вводятся в . За исключением стадии , на которой находят до несовпадений, на стадии для каждой строки требуется найти до несовпадений, а не до , как в алгоритме анализа текста.
pm[... ][1...min{ , }] = m + 1 r = j = for i = to b = 0 if i < j merge(i, r, j, b) if b < min{ } r = i extend(i, j, b)
|
Оценка работы
Теперь исследуем затраты времени на анализ текста. Если исключить вызовы процедур
и , каждая из итераций цикла анализа текста выполняется за фиксированное время, что дает в общей сложности время . Общее число операций, выполняемых процедурой во время вызовов равно , так как она проверяет каждый символ текста не больше одного раза. Процедура при каждом вызове обрабатывает массив и , которые в сумме имеют элементов. Время работы можно рассчитать, соотнеся операции с фиксированным временем с каждым из этих входов, что дает время счета для каждого вызова, равное . Таким образом, можно видеть, что общее время анализа текста составляет .Рассмотрим построение
. Используя аргументы, аналогичные применявшимся при проверке корректности процедуры merge, можно показать, что для нахождения требуемого количества несовпадений на стадии s требуется позиций, для которых выполняется условие B, и в особом случае, а именно, на стадии , требуется таких позиций.На каждой стадии
из стадий анализа образца цикл производит итераций . Если не считать время работы процедур и , каждая итерация требует фиксированного времени. Для всех итераций на шаге процедуре требуется время . Ранее было показано, что время работы пропорционально числу искомых несовпадений. Таким образом, каждый вызов занимает время , что равно . Таким образом, общее время для стадии равно = . Проведя суммирование по всем стадиям, получаем общее время счета . Таким образом, общие затраты времени, включающие предварительную обработку образца и анализ текста, равны .Пример
Пусть
, , .tm | 1 | 2 | 3 | x[1..m] | y[i+1..i+m] |
0 | 2 | 3 | 4 | tram | thet |
1 | 1 | 2 | 3 | tram | hetr |
2 | 1 | 2 | 3 | tram | etri |
3 | 3 | 4 | 5 | tram | trip |
4 | 1 | 2 | 3 | tram | ripp |
5 | 1 | 2 | 3 | tram | ippe |
6 | 1 | 2 | 3 | tram | pped |
7 | 1 | 2 | 3 | tram | pedt |
8 | 1 | 2 | 3 | tram | edtr |
9 | 1 | 2 | 3 | tram | dtra |
10 | 4 | 5 | 5 | tram | trap |
pm | 1 | 2 | 3 | 4 | 5 | x[1..m-i] | x[i+1..m] |
1 | 1 | 2 | 3 | 5 | 5 | tra | ram |
2 | 1 | 2 | 5 | 5 | 5 | tr | am |
3 | 1 | 5 | 5 | 5 | 5 | t | m |