Двусторонний алгоритм — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Характерные черты)
(Исправления)
Строка 1: Строка 1:
'''Двусторонний алгоритм''' (англ. ''Two Way algorithm'') — алгоритм [[Наивный алгоритм поиска подстроки в строке#Постановка задачи|поиска подстроки в строке]].
+
'''Двусторонний алгоритм''' (англ. ''Two Way algorithm'') — алгоритм [[Поиск подстроки в строке|поиска подстроки в строке]].
  
 
==Характерные черты==
 
==Характерные черты==
 
* требует упорядоченный алфавит,
 
* требует упорядоченный алфавит,
 
* этап предобработки занимает <math>O(m)</math> времени и константное количество памяти,
 
* этап предобработки занимает <math>O(m)</math> времени и константное количество памяти,
* этап поиска за время <math>O(n)</math>.
+
* этап поиска за время <math>O(n)</math>, где m {{---}} длина образца, а n {{---}} длина текста.
  
 
==Описание алгоритма==
 
==Описание алгоритма==
Строка <tex>x</tex> разбивается на две части <math>x_l</math> и <math>x_r</math> так, что <math>x=</math> <math>x_l</math> <math>x_r</math>. Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов <math>x_r</math> слева направо, и затем, если на этом первом этапе не происходит несовпадений, в сравнении символов <math>x_l</math> справа налево (второй этап).
+
Строка <tex>x</tex> разбивается на две части <math>u</math> и <math>v</math> так, что <math>x = uv</math>. Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов <math>v</math> слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов <math>u</math> справа налево (второй этап).
Фаза предобработки, таким образом, заключается в поиске подходящего разбиения <math>x_l</math> <math>x_r</math>.
+
Фаза предобработки, таким образом, заключается в поиске подходящего разбиения <tex>(u, v)</tex>.
 +
{{Определение
 +
|definition = <tex>(u, v)</tex> {{---}} '''разбиение''' строки <tex>x</tex>, если <tex>x = uv</tex>.
 +
}}
 +
 
 
{{Определение
 
{{Определение
 
|definition = Пусть <math>(u, v)</math> {{---}} разбиение <math>x</math>. '''Повторение''' в <math>(u, v)</math> {{---}} слово <math>w</math> такое, что для него выполнены следующие условия:
 
|definition = Пусть <math>(u, v)</math> {{---}} разбиение <math>x</math>. '''Повторение''' в <math>(u, v)</math> {{---}} слово <math>w</math> такое, что для него выполнены следующие условия:
# <math>w</math> {{---}} суффикс <math>u</math> или <math>u</math> {{---}} суффикс <math>w</math>;
+
# <math>w</math> {{---}} суффикс <math>u</math> или <math>u</math> {{---}} суффикс <math>w</math>.
# <math>w</math> {{---}} префикс <math>v</math> или <math>v</math> {{---}} префикс <math>w</math>.
+
# <math>w</math> {{---}} префикс <math>v</math> или <math>v</math> {{---}} префикс <math>w</math>.}}
  
Длина повторения в <math>(u, v)</math> называется '''локальным периодом'''; наименьший локальный период записывается как <math>r(u, v)</math>.
+
{{Определение
 +
|definition = Длина повторения в <math>(u, v)</math> называется '''локальным периодом'''; наименьший локальный период записывается как <math>r(u, v)</math>.
  
Каждое разбиение <math>x</math> на <math>(u, v)</math> имеет как минимум одно повторение. Очевидно, что <math>1 \leqslant r(u, v) \leqslant |x|</math>
+
Каждое разбиение <math>x</math> на <math>(u, v)</math> имеет как минимум одно повторение. Очевидно, что <math>1 \leqslant r(u, v) \leqslant |x|</math>}}
Разбиение <math>x</math> на <math>(u, v)</math> такое, что <math>r(u, v) = per(x)</math> называется '''критическим разбиением''' <math>x</math>.
+
 
 +
{{Определение
 +
|definition = Разбиение <math>x</math> на <math>(u, v)</math> такое, что <math>r(u, v) = per(x)</math> называется '''критическим разбиением''' <math>x</math>.
 
}}
 
}}
  
Если <math>(u, v)</math> {{---}} критическое разбиение <math>x</math>, то на позиции <math>|u|</math> в <math>x</math> общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение <math>(x_l, x_r)</math> такое, что <math>|x_l| < per(x)</math> и длина <math>|x_l|</math> минимальна.
+
Если <math>(u, v)</math> {{---}} критическое разбиение <math>x</math>, то на позиции <math>|u|</math> в <math>x</math> общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение <math>(u, v)</math> такое, что <math>|u| < per(x)</math> и длина <math>|u|</math> минимальна.
Чтобы найти критическое разбиение <math>(x_l, x_r)</math> мы сперва вычислим <math>z</math> {{---}} максимальный суффикс <math>x</math> в порядке <math>\leqslant</math> и максимальный суффикс <math>\widetilde{z}</math> для обратного порядка <math>\geqslant</math>. Затем <math>(x_l, x_r)</math> выбираются так, что <math>|x_l| = \max(|z|, |\widetilde{z}|)</math>.
+
Чтобы найти критическое разбиение <math>(u, v)</math> мы сперва вычислим <math>z</math> {{---}} максимальный суффикс <math>x</math> в лексикографическом порядке, характерном для заданного алфавита <math>\leqslant</math> и максимальный суффикс <math>\widetilde{z}</math> для обратного лексикографического порядка <math>\geqslant</math>. Затем <math>(u, v)</math> выбираются так, что <math>|u| = \max(|z|, |\widetilde{z}|)</math>.
  
Фаза поиска в двустороннем алгоритме состоит из сравнения символов <math>x_r</math> слева направо и символов <math>x_l</math> справа налево. Когда происходит несовпадение при просмотре <math>k</math>-го символа в <math>x_r</math>, производится сдвиг длиной <math>k</math>. Когда происходит несовпадение при просмотре <math>x_l</math>, или когда образец встретился в строке, производится сдвиг длиной <math>per(x)</math>.
+
Фаза поиска в двустороннем алгоритме состоит из сравнения символов <math>v</math> слева направо и символов <math>u</math> справа налево. Когда происходит несовпадение при просмотре <math>k</math>-го символа в <math>v</math>, производится сдвиг длиной <math>k</math>. Когда происходит несовпадение при просмотре <math>u</math> или когда образец встретился в строке, производится сдвиг длиной <math>per(x)</math>.
 
Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной <math>per(x)</math>, длина совпадающего префикса образца в начале "окна" (а именно <math>m - per(x)</math>) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.
 
Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной <math>per(x)</math>, длина совпадающего префикса образца в начале "окна" (а именно <math>m - per(x)</math>) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.
  
Строка 77: Строка 84:
 
* [http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf  Оригинал статьи (M. Crochemore, D. Perrin)]
 
* [http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf  Оригинал статьи (M. Crochemore, D. Perrin)]
 
* [http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260  Краткое описание алгоритма, пример работы]
 
* [http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260  Краткое описание алгоритма, пример работы]
 +
 +
== См. также ==
 +
* [[Алгоритм Кнута-Морриса-Пратта ]]
 +
* [[Алгоритм Бойера-Мура]]
  
 
[[Категория:Алгоритмы и структуры данных]]
 
[[Категория:Алгоритмы и структуры данных]]
 
[[Категория:Поиск подстроки в строке]]
 
[[Категория:Поиск подстроки в строке]]

Версия 22:25, 15 июня 2015

Двусторонний алгоритм (англ. Two Way algorithm) — алгоритм поиска подстроки в строке.

Характерные черты

  • требует упорядоченный алфавит,
  • этап предобработки занимает [math]O(m)[/math] времени и константное количество памяти,
  • этап поиска за время [math]O(n)[/math], где m — длина образца, а n — длина текста.

Описание алгоритма

Строка [math]x[/math] разбивается на две части [math]u[/math] и [math]v[/math] так, что [math]x = uv[/math]. Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов [math]v[/math] слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов [math]u[/math] справа налево (второй этап). Фаза предобработки, таким образом, заключается в поиске подходящего разбиения [math](u, v)[/math].

Определение:
[math](u, v)[/math]разбиение строки [math]x[/math], если [math]x = uv[/math].


Определение:
Пусть [math](u, v)[/math] — разбиение [math]x[/math]. Повторение в [math](u, v)[/math] — слово [math]w[/math] такое, что для него выполнены следующие условия:
  1. [math]w[/math] — суффикс [math]u[/math] или [math]u[/math] — суффикс [math]w[/math].
  2. [math]w[/math] — префикс [math]v[/math] или [math]v[/math] — префикс [math]w[/math].


Определение:
Длина повторения в [math](u, v)[/math] называется локальным периодом; наименьший локальный период записывается как [math]r(u, v)[/math]. Каждое разбиение [math]x[/math] на [math](u, v)[/math] имеет как минимум одно повторение. Очевидно, что [math]1 \leqslant r(u, v) \leqslant |x|[/math]


Определение:
Разбиение [math]x[/math] на [math](u, v)[/math] такое, что [math]r(u, v) = per(x)[/math] называется критическим разбиением [math]x[/math].


Если [math](u, v)[/math] — критическое разбиение [math]x[/math], то на позиции [math]|u|[/math] в [math]x[/math] общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение [math](u, v)[/math] такое, что [math]|u| \lt per(x)[/math] и длина [math]|u|[/math] минимальна. Чтобы найти критическое разбиение [math](u, v)[/math] мы сперва вычислим [math]z[/math] — максимальный суффикс [math]x[/math] в лексикографическом порядке, характерном для заданного алфавита [math]\leqslant[/math] и максимальный суффикс [math]\widetilde{z}[/math] для обратного лексикографического порядка [math]\geqslant[/math]. Затем [math](u, v)[/math] выбираются так, что [math]|u| = \max(|z|, |\widetilde{z}|)[/math].

Фаза поиска в двустороннем алгоритме состоит из сравнения символов [math]v[/math] слева направо и символов [math]u[/math] справа налево. Когда происходит несовпадение при просмотре [math]k[/math]-го символа в [math]v[/math], производится сдвиг длиной [math]k[/math]. Когда происходит несовпадение при просмотре [math]u[/math] или когда образец встретился в строке, производится сдвиг длиной [math]per(x)[/math]. Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной [math]per(x)[/math], длина совпадающего префикса образца в начале "окна" (а именно [math]m - per(x)[/math]) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.

Псевдокод

function twoWaySearch(String pattern, String text):
    n = pattern.length
    [math]\langle[/math]len1, p1[math]\rangle[/math] [math]\leftarrow[/math] maxSuffix(pattern, [math]\leqslant[/math])
    [math]\langle[/math]len2, p2[math]\rangle[/math] [math]\leftarrow[/math] maxSuffix(pattern, [math]\geqslant[/math])
    if len1 [math]\geqslant[/math] len2
        len = len1
        p = p1
    else
        len = len2
        p = p2
    occurences = [math]\varnothing[/math]
    pos [math]\leftarrow[/math] 0
    memPrefix [math]\leftarrow[/math] 0
    if len < n/2 and pattern[1[math]\ldots[/math]len] [math]-[/math] суффикс pattern[len + 1[math]\ldots[/math]len + p]
        while pos + n [math]\leqslant[/math] text.length
            i [math]\leftarrow \max[/math](l, memPrefix) + 1
            while i [math]\leqslant[/math] n and pattern[i] [math]=[/math] text[pos + i]
                i++
            if i [math]\leqslant[/math] n
                pos [math]\leftarrow[/math] pos + [math]\max[/math](i [math]-[/math] len, memPrefix [math]-[/math] p [math]+[/math] 1)
                memPrefix [math]\leftarrow[/math] 0
            else
                j [math]\leftarrow[/math] l
                while j [math] \gt  [/math] memPrefix and pattern[j] [math]=[/math] text[pos + j]
                    j--
                if j [math]\leqslant[/math] memPrefix
                    pos [math]\rightarrow[/math] occurences
                pos [math]\leftarrow[/math] pos [math]+[/math] p
                memPrefix [math]\leftarrow[/math] n [math]-[/math] p
    else
        q [math]\leftarrow \max[/math](len, n [math]-[/math] len) [math]+[/math] 1
        while pos + n [math]\leqslant[/math] text.length
            i [math]\leftarrow[/math] len + 1
            while i [math]\leqslant[/math] n and pattern[i] [math]=[/math] text[pos [math]+[/math] i]
                i++
            if i [math]\leqslant[/math] n
                pos [math]\leftarrow[/math] pos [math]+[/math] i [math]-[/math] len
            else
                j [math]\leftarrow[/math] len
                while j [math] \gt  [/math] 0 and pattern[j] [math]=[/math] text[pos [math]+[/math] j]
                    j--
                if j [math]=[/math] 0
                    pos [math]\rightarrow[/math] occurences
                pos [math]\leftarrow[/math] pos [math]+[/math] q
    return occurences

Источники информации

См. также