Двусторонний алгоритм — различия между версиями
 (→Псевдокод)  | 
				|||
| Строка 37: | Строка 37: | ||
  '''function''' twoWaySearch(String pattern, String text):  |   '''function''' twoWaySearch(String pattern, String text):  | ||
      <font color=green>//предобработка <tex>-</tex> вычисление критической позиции (в которой строка делится на <tex>u</tex> и <tex>v</tex>)</font>  |       <font color=green>//предобработка <tex>-</tex> вычисление критической позиции (в которой строка делится на <tex>u</tex> и <tex>v</tex>)</font>  | ||
| − |       <tex>\langle</tex>l1, p1<tex>\rangle</tex>   | + |       <tex>\langle</tex>l1, p1<tex>\rangle</tex> = maxSuffix(pattern, <tex>\leqslant</tex>)  | 
| − |       <tex>\langle</tex>l2, p2<tex>\rangle</tex>   | + |       <tex>\langle</tex>l2, p2<tex>\rangle</tex> = maxSuffix(pattern, <tex>\geqslant</tex>)  | 
      '''if''' l1 <tex>\geqslant</tex> l2  |       '''if''' l1 <tex>\geqslant</tex> l2  | ||
          l = l1  |           l = l1  | ||
| Строка 47: | Строка 47: | ||
      <font color=green>//<tex>p</tex> <tex>-</tex> период <tex>x</tex>, <tex>l</tex> <tex>-</tex> такая критическая позиция, что <tex>l<p</tex></font>  |       <font color=green>//<tex>p</tex> <tex>-</tex> период <tex>x</tex>, <tex>l</tex> <tex>-</tex> такая критическая позиция, что <tex>l<p</tex></font>  | ||
      occurences = <tex>\varnothing</tex>  |       occurences = <tex>\varnothing</tex>  | ||
| − |       pos   | + |       pos = 0  | 
| − |       memPrefix   | + |       memPrefix = 0  | 
      '''while''' pos + pattern.length <tex>\leqslant</tex> text.length  |       '''while''' pos + pattern.length <tex>\leqslant</tex> text.length  | ||
      <font color=green>//первый этап: просмотр <tex>v</tex> слева направо</font>  |       <font color=green>//первый этап: просмотр <tex>v</tex> слева направо</font>  | ||
| − |           i <tex>  | + |           i = <tex>\max</tex>(l, memPrefix) + 1  | 
          '''while''' i <tex>\leqslant</tex> pattern.length '''and''' pattern[i] <tex>=</tex> text[pos + i]  |           '''while''' i <tex>\leqslant</tex> pattern.length '''and''' pattern[i] <tex>=</tex> text[pos + i]  | ||
              i++  |               i++  | ||
          '''if''' i <tex>\leqslant</tex> pattern.length  |           '''if''' i <tex>\leqslant</tex> pattern.length  | ||
| − |               pos   | + |               pos = pos + <tex>\max</tex>(i <tex>-</tex> l, memPrefix <tex>-</tex> p <tex>+</tex> 1)  | 
| − |               memPrefix   | + |               memPrefix = 0  | 
          '''else'''  |           '''else'''  | ||
              <font color=green>//второй этап: просмотр <tex>u</tex> справа налево</font>  |               <font color=green>//второй этап: просмотр <tex>u</tex> справа налево</font>  | ||
| − |               j   | + |               j = l  | 
              '''while''' j <tex> > </tex> memPrefix '''and''' pattern[j] <tex>=</tex> text[pos + j]  |               '''while''' j <tex> > </tex> memPrefix '''and''' pattern[j] <tex>=</tex> text[pos + j]  | ||
                  j--  |                   j--  | ||
              '''if''' j <tex>\leqslant</tex> memPrefix  |               '''if''' j <tex>\leqslant</tex> memPrefix  | ||
| − |                   pos   | + |                   occurences = pos    | 
| − |               pos   | + |               pos = pos <tex>+</tex> p  | 
| − |               memPrefix   | + |               memPrefix = pattern.length <tex>-</tex> p  | 
      '''return''' occurences  |       '''return''' occurences  | ||
Версия 14:44, 27 апреля 2016
Двусторонний алгоритм (англ. Two Way algorithm) — алгоритм поиска подстроки в строке.
Содержание
Характерные черты
- требует упорядоченный алфавит,
 - этап предобработки занимает времени и константное количество памяти,
 - этап поиска за время , где m — длина образца, а n — длина текста.
 
Описание алгоритма
Строка разбивается на две части и так, что . Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов справа налево (второй этап). Фаза предобработки, таким образом, заключается в поиске подходящего разбиения .
| Определение: | 
| — разбиение строки , если . | 
| Определение: | 
Пусть  — разбиение . Повторение в  — слово  такое, что для него выполнены следующие условия:
  | 
| Определение: | 
| Длина повторения в называется локальным периодом; наименьший локальный период записывается как . Каждое разбиение на имеет как минимум одно повторение. Очевидно, что | 
| Определение: | 
| Разбиение на такое, что называется критическим разбиением . | 
Если  — критическое разбиение , то на позиции  в  общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение  такое, что  и длина  минимальна.
Чтобы найти критическое разбиение  мы сперва вычислим  — максимальный суффикс  в лексикографическом порядке, характерном для заданного алфавита  и максимальный суффикс  для обратного лексикографического порядка . Затем  выбираются так, что .
Фаза поиска в двустороннем алгоритме состоит из сравнения символов слева направо и символов справа налево. Когда происходит несовпадение при просмотре -го символа в , производится сдвиг длиной . Когда происходит несовпадение при просмотре или когда образец встретился в строке, производится сдвиг длиной . Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной , длина совпадающего префикса образца в начале "окна" (а именно ) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.
Псевдокод
function twoWaySearch(String pattern, String text):
    //предобработка  вычисление критической позиции (в которой строка делится на  и )
    l1, p1 = maxSuffix(pattern, )
    l2, p2 = maxSuffix(pattern, )
    if l1  l2
        l = l1
        p = p1
    else
        len = l2
        p = p2
    //  период ,   такая критическая позиция, что 
    occurences = 
    pos = 0
    memPrefix = 0
    while pos + pattern.length  text.length
    //первый этап: просмотр  слева направо
        i = (l, memPrefix) + 1
        while i  pattern.length and pattern[i]  text[pos + i]
            i++
        if i  pattern.length
            pos = pos + (i  l, memPrefix  p  1)
            memPrefix = 0
        else
            //второй этап: просмотр  справа налево
            j = l
            while j  memPrefix and pattern[j]  text[pos + j]
                j--
            if j  memPrefix
                occurences = pos  
            pos = pos  p
            memPrefix = pattern.length  p
    return occurences
Ценность алгоритма
Двусторонний алгоритм отчасти похож на алгоритм Бойера-Мура (просмотр символов справа налево и сдвиг позиции при несовпадении на втором этапе), и в лучшем случае работает немногим медленнее его, а в худшем — значительно превосходит[1], но главное отличие двустороннего алгоритма от алгоритмов Кнута-Морриса-Пратта и Бойера-Мура — константное количество затрачиваемой дополнительной памяти.
См. также
Источники информации
Примечания
- ↑ Journal of the Association for Computing Machinery, Vol. 38, No, 1, July 1991 Оценки скорости работы