Алгоритм Колусси — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
Строка 2: Строка 2:
 
Отметим, что нумерация символов строк и элементов массива у нас начинается с <tex>0</tex>.
 
Отметим, что нумерация символов строк и элементов массива у нас начинается с <tex>0</tex>.
  
Сначала построим массивы: Kmp, Kmin, Rmin, Shift.
+
Обозначим <tex>Kmp</tex> [[Префикс-функция|префикс-функцию]]. При этом она должна быть определена для <tex>x[0] \dots x[m-1]</tex> и иметь значение <tex>-1</tex> по умолчанию.
  
 
{{Определение
 
{{Определение
|definition=Обозначим за <tex>\mathrm{Kmin}(i)=i - \mathrm{Kmp}(i)</tex> функцию, возвращающую количество прыжков для позиций <tex>i</tex>, которые являются полными.
+
|definition=
}}
+
Для всех <tex>i</tex> таких, что <tex>0 \leqslant i \leqslant m-1</tex> выполняется:<br/>
 
+
<tex>\mathrm{Kmin}(i) = \begin{cases}
{{Определение
+
   d,  & \mbox{if }  d > 0\ and\ x[0 \dots i-1-d]=x[d \dots i-1]\ and\ x[i-d] \neq x[i]\\
|definition=Обозначим за <tex>\mathrm{Rmin}(i)</tex> функцию, возвращающую наименьший из периодов шаблона, которые больше позиции <tex>i</tex>, для позиций, которые являются пустыми.
+
   0, & \mbox{otherwise}
}}
 
 
 
{{Определение
 
|definition=Функция сдвига <tex>\mathrm{Shift}(i) = \begin{cases}
 
   \mathrm{Rmin}(i),  & \mbox{if }  \mathrm{Kmp}(i) = -1\\
 
   \mathrm{Kmin}(i), & \mbox{otherwise}
 
 
\end{cases}</tex>
 
\end{cases}</tex>
 
}}
 
}}
 
 
 
 
  
  
 
==Псевдокод==
 
==Псевдокод==
 
 
'''Наивный вариант'''
 
'''Наивный вариант'''
 
   '''int'''[] buildHmax('''char'''[] x, '''int''' m):
 
   '''int'''[] buildHmax('''char'''[] x, '''int''' m):
Строка 57: Строка 46:
 
На каждой итерации цикла увеличивается либо переменная <tex>i</tex>, либо <tex>k</tex> (или переменная <tex>q</tex>, которая используется в конечном счете для обновления <tex>k</tex>). Поскольку <tex>i = 1</tex> и <tex>k = 1</tex> в начале и <tex>i < k = m + 1</tex> в конце алгоритма, количество итераций алгоритма не превосходит <tex>2 \cdot m</tex>. Следовательно функция требует <tex>O(m)</tex> времени и памяти.
 
На каждой итерации цикла увеличивается либо переменная <tex>i</tex>, либо <tex>k</tex> (или переменная <tex>q</tex>, которая используется в конечном счете для обновления <tex>k</tex>). Поскольку <tex>i = 1</tex> и <tex>k = 1</tex> в начале и <tex>i < k = m + 1</tex> в конце алгоритма, количество итераций алгоритма не превосходит <tex>2 \cdot m</tex>. Следовательно функция требует <tex>O(m)</tex> времени и памяти.
  
Функция для построения массива <tex>Kmin</tex>.
+
Функция для построения массива <tex>\mathrm{Kmin}</tex>.
 
   '''int'''[] buildKmin('''int'''[] hmax, '''int''' m)
 
   '''int'''[] buildKmin('''int'''[] hmax, '''int''' m)
 
       '''int''' kmin[m]
 
       '''int''' kmin[m]
Строка 65: Строка 54:
 
       '''return''' kmin
 
       '''return''' kmin
  
Функция для построения массива <tex>Rmin</tex>.
+
Функция для построения массива <tex>\mathrm{Rmin}</tex>.
 
   '''int'''[] buildRmin('''int'''[] hmax, '''int'''[] kmin, '''int''' m)
 
   '''int'''[] buildRmin('''int'''[] hmax, '''int'''[] kmin, '''int''' m)
 
       '''int''' rmin[m]
 
       '''int''' rmin[m]
Строка 71: Строка 60:
 
       '''for''' i = m - 1 .. 0
 
       '''for''' i = m - 1 .. 0
 
         '''if''' hmax[i + 1] == m
 
         '''if''' hmax[i + 1] == m
 +
            <font color="green">// <tex>r</tex> {{---}} первое число большее, чем <tex>i</tex> и такое, что шаблон имеет период <tex>r</tex></font>
 
             r = i + 1
 
             r = i + 1
 
         '''if''' kmin[i] == 0
 
         '''if''' kmin[i] == 0
Строка 77: Строка 67:
 
             rmin[i] = 0
 
             rmin[i] = 0
 
       return rmin
 
       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
 +
        shift[i] = kmin[h[i]]
 +
      '''for''' i = nd + 1 .. m - 1
 +
        shift[i] = rmin[h[i]]
 +
      shift[m] = rmin[0]
 +
      '''return''' shift
 +
 +
Функция для построения массива <tex>\mathrm{Next}</tex>.
 +
  
 
==Асимптотики==
 
==Асимптотики==
Строка 86: Строка 89:
 
==Сравнение с другими алгоритмами==
 
==Сравнение с другими алгоритмами==
 
===Достоинства===
 
===Достоинства===
 +
* Поиск выполняется за <tex>O(n)</tex> в отличие от [[Алгоритм Кнута-Морриса-Пратта|алгоритма Кнута-Морриса-Пратта]], поиск в котором занимается <tex>O(n+m)</tex>, что помогает уменьшить константу при <tex>m~n</tex>.
 +
* Фаза предобработки выполняется за <tex>O(m)</tex> в отличие от [[Алгоритм Бойера-Мура|алгоритма Бойера-Мура]], где в наилучшем случае можно получить время <tex>O(n+|\Sigma|)</tex>, что плохо при больших алфавитах.
 
===Недостатки===
 
===Недостатки===
==Ссылки==
+
* Сложность реализации.
 +
==Источники==
 +
* 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]
 
* [http://www-igm.univ-mlv.fr/~lecroq/string/node10.html Colussi algorithm]
 
* [http://alg.csie.ncnu.edu.tw/course/StringMatching/Colussi.ppt Colussi.ppt]
 
* [http://alg.csie.ncnu.edu.tw/course/StringMatching/Colussi.ppt Colussi.ppt]

Версия 00:58, 15 мая 2014

Алгоритм

Отметим, что нумерация символов строк и элементов массива у нас начинается с [math]0[/math].

Обозначим [math]Kmp[/math] префикс-функцию. При этом она должна быть определена для [math]x[0] \dots x[m-1][/math] и иметь значение [math]-1[/math] по умолчанию.


Определение:
Для всех [math]i[/math] таких, что [math]0 \leqslant i \leqslant m-1[/math] выполняется:
[math]\mathrm{Kmin}(i) = \begin{cases} d, & \mbox{if } d \gt 0\ and\ x[0 \dots i-1-d]=x[d \dots i-1]\ and\ x[i-d] \neq x[i]\\ 0, & \mbox{otherwise} \end{cases}[/math]


Псевдокод

Наивный вариант

  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

Явная реализация по определению, очевидно, работает за [math]O(m^2)[/math] и требует [math]O(m)[/math] памяти.

Улучшенный вариант

  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

На каждой итерации цикла увеличивается либо переменная [math]i[/math], либо [math]k[/math] (или переменная [math]q[/math], которая используется в конечном счете для обновления [math]k[/math]). Поскольку [math]i = 1[/math] и [math]k = 1[/math] в начале и [math]i \lt k = m + 1[/math] в конце алгоритма, количество итераций алгоритма не превосходит [math]2 \cdot m[/math]. Следовательно функция требует [math]O(m)[/math] времени и памяти.

Функция для построения массива [math]\mathrm{Kmin}[/math].

  int[] buildKmin(int[] hmax, int m)
     int kmin[m]
     for i = m .. 1
        if hmax[i] < m
           kmin[hmax[i]] = i
     return kmin

Функция для построения массива [math]\mathrm{Rmin}[/math].

  int[] buildRmin(int[] hmax, int[] kmin, int m)
     int rmin[m]
     int r = 0
     for i = m - 1 .. 0
        if hmax[i + 1] == m
           // [math]r[/math] — первое число большее, чем [math]i[/math] и такое, что шаблон имеет период [math]r[/math]
           r = i + 1
        if kmin[i] == 0
           rmin[i] = r
        else
           rmin[i] = 0
     return rmin

Функция для построение массива [math]\mathrm{Shift}[/math].

  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

Функция для построения массива [math]\mathrm{Next}[/math].


Асимптотики

  • partitions the set of pattern positions into two disjoint subsets; the positions in the first set are scanned from left to right and when no mismatch occurs the positions of the second subset are scanned from right to left;
  • preprocessing phase in [math]O(m)[/math] time and space complexity;
  • searching phase in [math]O(n)[/math] time complexity;
  • performs [math]3/2n[/math] text character comparisons in the worst case.

Сравнение с другими алгоритмами

Достоинства

  • Поиск выполняется за [math]O(n)[/math] в отличие от алгоритма Кнута-Морриса-Пратта, поиск в котором занимается [math]O(n+m)[/math], что помогает уменьшить константу при [math]m~n[/math].
  • Фаза предобработки выполняется за [math]O(m)[/math] в отличие от алгоритма Бойера-Мура, где в наилучшем случае можно получить время [math]O(n+|\Sigma|)[/math], что плохо при больших алфавитах.

Недостатки

  • Сложность реализации.

Источники

  • COLUSSI L., 1991, Correctness and efficiency of the pattern matching algorithms, Information and Computation 95(2):225-251.
  • Colussi algorithm
  • Colussi.ppt