Изменения

Перейти к: навигация, поиск

Алгоритм Колусси

2152 байта добавлено, 14:52, 2 февраля 2016
м
Источники информации: добавлена категория
Алгоритм, разработанный Ливио Колусси, профессором итальянского университета Padova, и опубликованный им в 1991 году, является продолжением работы над оптимизацией [[Алгоритм Кнута-Морриса-Пратта|алгоритма Кнута-Морриса-Пратта]]. Предназначен для поиска одной подстроки в нескольких текстаходном тексте.
==Алгоритм==
Алгоритм сравнивает символы шаблона <tex>x</tex> один за другим с символами исходной строки <tex>y</tex>. Для сдвигов шаблона относительно исходной строки применяются вспомогательные функции, описанные ниже.
 
Пусть <tex>|y|=n</tex> и <tex>|x|=m</tex>.
Обозначим за <tex>\mathrm{Kmp}</tex> {{---}} [[Префикс-функция|префикс-функцию]], но при этом она определена для <tex>x[0] \dots x[m-1]</tex> и имеет значение <tex>-1</tex> по умолчанию.
Отметим, что нумерация символов строк и элементов массива у нас начинается с <tex>0</tex>.
Множество всех позиций шаблона разделим на два (дизъюнктных) непересекающихся множества. Тогда каждая попытка сравнения шаблона с исходной строкой после очередного сдвига состоит из двух случаев.
{{Определение
|definition=
В первом случае сравнения выполняются слева направо с символами текста, выровненными с шаблоном в позиции, для которой значение функции <tex>\mathrm{Kmp}(i)</tex> строго больше <tex>-1</tex>. Такие позиции будем называть '''насыщенными''' (англ. ''noholes'').
}}
{{Определение
|definition=
Во втором случае будут производиться сравнения в оставшихся позициях справа налево. Такие позиции будем называть '''ненасыщенными''' (англ. ''holes'').
}}
{{Определение
|definition=
Обозначим за <tex>\mathrm{first}(u)</tex> наименьший наименьшее число <tex>v</tex> такое, что <tex>u \leqslant h[v]</tex>.
}}
Теперь рассмотрим 2 случаяхслучая, возможных при очередной попытке сравнения шаблона с подстрокой из текста. Допустим, что шаблон <tex>x</tex> выровнен с подстрокой <tex>y[j \dots j+m-1]</tex>.
=== Первая Первый случай ===
Рассмотрим случай, когда <tex>x[h[k]]=y[j+h[k]]</tex> для <tex>0 \leqslant k < r < nd</tex> и <tex>x[h[r]] \neq y[j+h[r]]</tex>.
Кроме того равенство <tex>x[h[k]]=y[j’+h[k]]</tex> для всех <tex>k : 0 \leqslant k < \mathrm{first}(h[r]-\mathrm{K_{min}}(h[r]))</tex> означает, что сравнения могут продолжены с символов <tex>x[h[\mathrm{first}(h[r] - \mathrm{K_{min}}(h[r]))]]</tex> и <tex>y[j'+h[\mathrm{first}(h[r]-\mathrm{K_{min}}(h[r]))]]</tex>.
 
[[Файл:colussi-algorithm-1.png|450px|thumb|center|Несовпадение в насыщенной позиции. Насыщенные позиции обозначены черными кругами и сравниваются слева направо.]]
=== Второй случай ===
Теперь рассмотрим ситуацию, когда <tex>x[h[k]]=y[j+h[k]]</tex> для <tex>0 \leqslant k < r</tex> и <tex>x[h[r]] \neq y[j+h[r]]</tex> для <tex>nd \leqslant r < m</tex>.
Пусть <tex>j' = j + \mathrm{R_{min}}(h[r])</tex> позиций вправо.
Тогда нет вхождений шаблона <tex>x</tex>, начиная с <tex>y[j \dots j']</tex> и <tex>x</tex> может быть сдвинут на <tex>\mathrm{R_{min}}(h[r])</tex>позиций вправо.
Кроме того равенство <tex>x[0 \dots m-1-\mathrm{R_{min}}(h[r])]=y[j' \dots j+m-1]</tex> означает, что сравнения могут продолжены с символов <tex>x[h[\mathrm{first}(m-1-\mathrm{R_{min}}(h[r]))]]</tex> и <tex>y[j'+h[\mathrm{first}(m-1-\mathrm{R_{min}}(h[r]))]]</tex>.
 
[[Файл:colussi-algorithm-2.png|450px|thumb|center|Несовпадение в ненасыщенной позиции. Ненасыщенные позиции обозначены белыми кругами и сравниваются справа налево.]]
=== Предварительные вычисления ===
* <tex>\mathrm{shift}(m)=\mathrm{R_{min}}(0)</tex> и <tex>\mathrm{next}(m)=\mathrm{nhd0}(m-\mathrm{R_{min}}(h[m-1]))</tex>.
Таким образом, при возникновении несовпадения между <tex>x[h[r]]</tex> и <tex>y[j+h[r]]</tex> окно сравнения должно быть сдвинуто на <tex>\mathrm{shift}(r)</tex> и сравнения могут быть продолжены с позиции <tex>h[\mathrm{next[}(r)]] </tex> шаблона <tex>x</tex>.
==Псевдокод==
Функция для построения массива <tex>\mathrm{H_{max}}</tex>.
 
'''Наивный вариант'''
'''int'''[] buildHmax('''char'''[] x, '''int''' m):
i++
hmax[k] = i
'''return ''' hmax
Явная реализация по определению, очевидно, работает за <tex>O(m^2)</tex> и требует <tex>O(m)</tex> памяти.
'''while''' k <= m
'''while''' x[i] == x[i - k]
i++;
hmax[k] = i
'''int''' q = k + 1
q++
k = q
'''if ''' k == i + 1
i = k
'''return''' hmax
Функция для построения массива <tex>\mathrm{K_{min}}</tex>.
'''int'''[] buildKmin('''int'''[] hmax, '''int''' m):
'''int''' kmin[m]
'''for''' i = m .. 1
Функция для построения массива <tex>\mathrm{R_{min}}</tex>.
'''int'''[] buildRmin('''int'''[] hmax, '''int'''[] kmin, '''int''' m):
'''int''' rmin[m]
'''int''' r = 0
'''else'''
rmin[i] = 0
'''return ''' rmin
Функция для построение массива <tex>\mathrm{shift}</tex>.
'''int'''[] buildShift('''int'''[] kmin, '''int'''[] rmin, '''int'''[] h, '''int''' nd, '''int''' m):
'''int''' shift[m + 1]
'''for''' i = 0 .. nd
Функция для построения массива <tex>\mathrm{next}</tex>.
'''int'''[] buildNext('''int'''[] kmin, '''int'''[] rmin, '''int'''[] h, '''int''' nd, '''int''' m): <font color="green">// Вычисление массива <tex>\mathrm{nhd0}</tex></font>
'''int''' nhd0[m]
'''int''' s = 0
'''for''' i = 0 .. m - 1
nhd0[i] = s if kmin[i] > 0
++s
<font color="green">// Вычисление массива <tex>\mathrm{next}</tex></font> '''int'''[] next = new int[m + 1]
'''for''' i = 0 .. nd
next[i] = nhd0[h[i] - kmin[h[i]]]
next[m] = nhd0[m - rmin[h[m - 1]]]
'''return''' next
 
Основная функция алгоритма Колусси.
'''function''' colussi('''char'''[] x, '''char'''[] y):
'''int''' n = length(y)
'''int''' m = length(x)
<font color="green">// Предварительные вычисления</font>
'''int'''[] hmax = buildHmax(x, m)
'''int'''[] kmin = buildKmin(hmax, m)
'''int'''[] rmin = buildRmin(hmax, kmin, m)
<font color="green">// Построение массива <tex>h</tex></font>
'''int''' h[m]
'''int''' s = -1
'''int''' r = m
'''for''' i = 0 .. m - 1
'''if''' kmin[i] == 0
h[--r] = i
'''else'''
h[++s] = i
'''int''' nd = s
'''int'''[] shift = buildShift(kmin, rmin, h, nd, m)
'''int'''[] next = buildNext(kmin, rmin, h, nd, m)
<font color="green">// Поиск подстроки</font>
'''int''' i = 0
'''int''' j = 0
'''int''' last = -1
'''while''' j <= n - m
'''while''' i < m '''and''' last < j + h[i] '''and''' x[h[i]] == y[j + h[i]]
++i
'''if''' i >= m '''or''' last >= j + h[i]
OUTPUT(j)
i = m
'''if''' i > nd
last = j + m - 1
j += shift[i]
i = next[i]
==Асимптотики==
===Недостатки===
* Сложность реализации.
 ==Источникиинформации==
* COLUSSI L., 1991, Correctness and efficiency of the pattern matching algorithms, Information and Computation 95(2):225-251.
* [http://www-igm.univ-mlv.fr/~lecroq/string/node10.html Colussi algorithm]
[[Категория: Дискретная математика и алгоритмы]]
[[Категория: Поиск подстроки в строке]]
[[Категория: Точный поиск]]

Навигация