Алгоритм Колусси — различия между версиями
Kabanov (обсуждение | вклад) (→Асимптотики) |
Kabanov (обсуждение | вклад) |
||
| Строка 1: | Строка 1: | ||
| + | Алгоритм, разработанный Ливио Колусси, профессором итальянского университета Padova, и опубликованный им в 1991 году, является продолжением работы над оптимизацией [[Алгоритм Кнута-Морриса-Пратта|алгоритма Кнута-Морриса-Пратта]]. Предназначен для поиска одной подстроки в нескольких текстах. | ||
| + | |||
==Алгоритм== | ==Алгоритм== | ||
| + | Алгоритм сравнивает символы шаблона <tex>x</tex> один за другим с символами исходной строки <tex>y</tex>. Для сдвигов шаблона относительно исходной строки применяются вспомогательные функции, описанные ниже. | ||
| + | |||
| + | Обозначим за <tex>\mathrm{Kmp}</tex> {{---}} [[Префикс-функция|префикс-функцию]], но при этом она определена для <tex>x[0] \dots x[m-1]</tex> и имеет значение <tex>-1</tex> по умолчанию. | ||
| + | |||
Отметим, что нумерация символов строк и элементов массива у нас начинается с <tex>0</tex>. | Отметим, что нумерация символов строк и элементов массива у нас начинается с <tex>0</tex>. | ||
| − | + | Множество всех позиций шаблона разделим на два (дизъюнктных) непересекающихся множества. Тогда каждая попытка сравнения шаблона с исходной строкой после очередного сдвига состоит из двух фаз. | |
| − | |||
| − | Множество всех позиций шаблона разделим на два непересекающихся множества. Тогда каждая попытка сравнения шаблона с исходной строкой состоит из двух фаз. | ||
{{Определение | {{Определение | ||
| Строка 18: | Строка 22: | ||
Такая стратегия предоставляет, как минимум, 2 преимущества: | Такая стратегия предоставляет, как минимум, 2 преимущества: | ||
* когда несовпадение появляется во время первой фазы, после соответствующего сдвига уже нет необходимости делать проверки в насыщенных позициях, которые были проверены на предыдущем шаге. | * когда несовпадение появляется во время первой фазы, после соответствующего сдвига уже нет необходимости делать проверки в насыщенных позициях, которые были проверены на предыдущем шаге. | ||
| − | * когда несовпадение появляется во время второй фазы, это означает, что суффикс шаблона совпал с | + | * когда несовпадение появляется во время второй фазы, это означает, что суффикс шаблона совпал с подстрокой исходной строки <tex>y</tex> и после соответствующего сдвига префикс шаблона будет все ещё совпадать с этой подстрокой, поэтому нет необходимости в повторной проверке. |
{{Определение | {{Определение | ||
|definition= | |definition= | ||
| − | Обозначим за <tex>\mathrm{K_{min}}(i) = \min\{k : \ x[0 \dots i-1-k]=x[d \dots i-1]\ and\ x[i-k] \neq x[i]\}</tex>. Функция <tex>\mathrm{K_{min}}</tex> определена для всех позиций <tex>i</tex>, у которых <tex>Kmp(i) \neq -1</tex>. | + | Обозначим за <tex>\mathrm{K_{min}}(i) = \min\{k : \ x[0 \dots i-1-k]=x[d \dots i-1]\ and\ x[i-k] \neq x[i]\}</tex>. Функция <tex>\mathrm{K_{min}}</tex> определена для всех позиций <tex>i</tex>, у которых <tex>\mathrm{Kmp}(i) \neq -1</tex>. |
}} | }} | ||
| Строка 28: | Строка 32: | ||
Очевидно, что для <tex>0 < i < m</tex> позиция <tex>i</tex>: | Очевидно, что для <tex>0 < i < m</tex> позиция <tex>i</tex>: | ||
| − | * насыщенная, если <tex>\mathrm{K_{min}}(i-1) \neq 0</tex> | + | * насыщенная, если <tex>\mathrm{K_{min}}(i-1) \neq 0</tex>, |
| − | * ненасыщенная, | + | * ненасыщенная, в остальных случаях. |
Обозначим за <tex>nd+1</tex> количество насыщенных позиций в шаблоне <tex>x</tex>. | Обозначим за <tex>nd+1</tex> количество насыщенных позиций в шаблоне <tex>x</tex>. | ||
| Строка 39: | Строка 43: | ||
{{Определение | {{Определение | ||
|definition= | |definition= | ||
| − | Обозначим за <tex>\mathrm{R_{min}}(i)</tex> наименьший период <tex>r</tex> шаблона <tex>x</tex> большего, чем <tex>i</tex>. Функция <tex>\mathrm{R_{min}}</tex> определена для всех позиций <tex>i</tex>, у которых <tex>Kmp(i) = -1</tex>. | + | Обозначим за <tex>\mathrm{R_{min}}(i)</tex> наименьший период <tex>r</tex> шаблона <tex>x</tex> большего, чем <tex>i</tex>. Функция <tex>\mathrm{R_{min}}</tex> определена для всех позиций <tex>i</tex>, у которых <tex>\mathrm{Kmp}(i) = -1</tex>. |
}} | }} | ||
| Строка 47: | Строка 51: | ||
}} | }} | ||
| − | Допустим, что шаблон <tex>x</tex> выровнен с <tex>y[j \dots j+m-1]</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>0 \leqslant k < r < nd</tex> и <tex>x[h[r]] \neq y[j+h[r]]</tex>. | ||
| Строка 58: | Строка 62: | ||
Кроме того <tex>x[h[k]]=y[j’+h[k]]</tex> для <tex>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>. | Кроме того <tex>x[h[k]]=y[j’+h[k]]</tex> для <tex>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>. | ||
| − | == Вторая | + | == Вторая случай == |
Теперь рассмотрим ситуацию, когда <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>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>. | ||
Версия 16:54, 13 июня 2014
Алгоритм, разработанный Ливио Колусси, профессором итальянского университета Padova, и опубликованный им в 1991 году, является продолжением работы над оптимизацией алгоритма Кнута-Морриса-Пратта. Предназначен для поиска одной подстроки в нескольких текстах.
Содержание
Алгоритм
Алгоритм сравнивает символы шаблона один за другим с символами исходной строки . Для сдвигов шаблона относительно исходной строки применяются вспомогательные функции, описанные ниже.
Обозначим за — префикс-функцию, но при этом она определена для и имеет значение по умолчанию.
Отметим, что нумерация символов строк и элементов массива у нас начинается с .
Множество всех позиций шаблона разделим на два (дизъюнктных) непересекающихся множества. Тогда каждая попытка сравнения шаблона с исходной строкой после очередного сдвига состоит из двух фаз.
| Определение: |
| В первой фазе сравнения выполняются слева направо с символами текста, выровненными с шаблоном в позиции, для которой значение функции строго больше . Такие позиции будем называть насыщенными (noholes). |
| Определение: |
| Вторая фаза состоит в сравнении в оставшихся позициях справа налево. Такие позиции будем называть ненасыщенными (holes). |
Такая стратегия предоставляет, как минимум, 2 преимущества:
- когда несовпадение появляется во время первой фазы, после соответствующего сдвига уже нет необходимости делать проверки в насыщенных позициях, которые были проверены на предыдущем шаге.
- когда несовпадение появляется во время второй фазы, это означает, что суффикс шаблона совпал с подстрокой исходной строки и после соответствующего сдвига префикс шаблона будет все ещё совпадать с этой подстрокой, поэтому нет необходимости в повторной проверке.
| Определение: |
| Обозначим за . Функция определена для всех позиций , у которых . |
Если , то периодичность шаблона заканчивается в позиции .
Очевидно, что для позиция :
- насыщенная, если ,
- ненасыщенная, в остальных случаях.
Обозначим за количество насыщенных позиций в шаблоне .
Массив содержит первыми элементами насыщенных позиций в возрастающем порядке и затем ненасыщенных в убывающем порядке, т.е.
- для всех насыщенная позиция и для .
- для всех ненасыщенная и для .
| Определение: |
| Обозначим за наименьший период шаблона большего, чем . Функция определена для всех позиций , у которых . |
| Определение: |
| Обозначим за наименьший число такое, что . |
Теперь рассмотрим 2 случаях, возможных при очередной попытке сравнения шаблона с подстрокой из текста. Допустим, что шаблон выровнен с подстрокой .
Первая случай
Рассмотрим случай, когда для и .
Пусть .
Тогда нет вхождений шаблона , начиная с и может быть сдвинут на позиций вправо.
Кроме того для означает, что сравнения могут продолжены с и .
Вторая случай
Теперь рассмотрим ситуацию, когда для и для .
Пусть позиций вправо.
Тогда нет вхождений шаблона , начиная с и может быть сдвинут на .
Кроме того означает, что сравнения могут продолжены с и .
Предварительные вычисления
Для вычисления значений будем использовать вспомогательную функцию .
| Определение: |
Обозначим за функцию, для которой выполняется:
|
| Определение: |
| Обозначим за количество насыщенных позиций строго меньших . |
Теперь мы можем определить два функции и :
- и для всех ;
- и для всех ;
- и .
Таким образом, при возникновении несовпадения между и окно сравнения должно быть сдвинуто на и сравнения могут быть продолжены с позиции h[next[r]] шаблона .
Псевдокод
Наивный вариант
int[] buildHmax(char[] x, int m):
int hmax[m + 1]
for k = 1 .. m
int i = k
while x[i] == x[i - k]
i++
hmax[k] = i
return hmax
Явная реализация по определению, очевидно, работает за и требует памяти.
Улучшенный вариант
int[] buildHmax(char[] x, int m):
int hmax[m + 1]
int i = 1
int k = 1
while k <= m
while x[i] == x[i - k]
i++;
hmax[k] = i
int q = k + 1
while hmax[q - k] + k < i
hmax[q] = hmax[q - k] + k
q++
k = q
if k == i + 1
i = k
return hmax
На каждой итерации цикла увеличивается либо переменная , либо (или переменная , которая используется в конечном счете для обновления ). Поскольку и в начале и в конце алгоритма, количество итераций алгоритма не превосходит . Следовательно функция требует времени и памяти.
Функция для построения массива .
int[] buildKmin(int[] hmax, int m)
int kmin[m]
for i = m .. 1
if hmax[i] < m
kmin[hmax[i]] = i
return kmin
Функция для построения массива .
int[] buildRmin(int[] hmax, int[] kmin, int m)
int rmin[m]
int r = 0
for i = m - 1 .. 0
if hmax[i + 1] == m
// — первое число большее, чем и такое, что шаблон имеет период
r = i + 1
if kmin[i] == 0
rmin[i] = r
else
rmin[i] = 0
return rmin
Функция для построение массива .
int[] buildShift(int[] kmin, int[] rmin, int[] h, int nd, int m)
int shift[m + 1]
for i = 0 .. nd
shift[i] = kmin[h[i]]
for i = nd + 1 .. m - 1
shift[i] = rmin[h[i]]
shift[m] = rmin[0]
return shift
Функция для построения массива .
Асимптотики
- Фаза предварительных вычислений занимает времени и памяти;
- В худшем случае поиск требует сравнений.
где — длина исходного текста, — длина шаблона
Сравнение с другими алгоритмами
Достоинства
- Поиск выполняется за в отличие от алгоритма Кнута-Морриса-Пратта, поиск в котором занимается , что помогает уменьшить константу при .
- Фаза предобработки выполняется за в отличие от алгоритма Бойера-Мура, где в наилучшем случае можно получить время , что плохо при больших алфавитах.
Недостатки
- Сложность реализации.
Источники
- COLUSSI L., 1991, Correctness and efficiency of the pattern matching algorithms, Information and Computation 95(2):225-251.
- Colussi algorithm
- Colussi.ppt