Двусторонний алгоритм — различия между версиями
Heatwave (обсуждение | вклад) (→Характерные черты) |
Heatwave (обсуждение | вклад) (Исправления) |
||
| Строка 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> | + | Строка <tex>x</tex> разбивается на две части <math>u</math> и <math>v</math> так, что <math>x = uv</math>. Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов <math>v</math> слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов <math>u</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>( | + | Если <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>( | + | Чтобы найти критическое разбиение <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> | + | Фаза поиска в двустороннем алгоритме состоит из сравнения символов <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) — алгоритм поиска подстроки в строке.
Характерные черты
- требует упорядоченный алфавит,
- этап предобработки занимает времени и константное количество памяти,
- этап поиска за время , где m — длина образца, а n — длина текста.
Описание алгоритма
Строка разбивается на две части и так, что . Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов справа налево (второй этап). Фаза предобработки, таким образом, заключается в поиске подходящего разбиения .
| Определение: |
| — разбиение строки , если . |
| Определение: |
Пусть — разбиение . Повторение в — слово такое, что для него выполнены следующие условия:
|
| Определение: |
| Длина повторения в называется локальным периодом; наименьший локальный период записывается как . Каждое разбиение на имеет как минимум одно повторение. Очевидно, что |
| Определение: |
| Разбиение на такое, что называется критическим разбиением . |
Если — критическое разбиение , то на позиции в общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение такое, что и длина минимальна.
Чтобы найти критическое разбиение мы сперва вычислим — максимальный суффикс в лексикографическом порядке, характерном для заданного алфавита и максимальный суффикс для обратного лексикографического порядка . Затем выбираются так, что .
Фаза поиска в двустороннем алгоритме состоит из сравнения символов слева направо и символов справа налево. Когда происходит несовпадение при просмотре -го символа в , производится сдвиг длиной . Когда происходит несовпадение при просмотре или когда образец встретился в строке, производится сдвиг длиной . Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной , длина совпадающего префикса образца в начале "окна" (а именно ) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.
Псевдокод
function twoWaySearch(String pattern, String text):
n = pattern.length
len1, p1 maxSuffix(pattern, )
len2, p2 maxSuffix(pattern, )
if len1 len2
len = len1
p = p1
else
len = len2
p = p2
occurences =
pos 0
memPrefix 0
if len < n/2 and pattern[1len] суффикс pattern[len + 1len + p]
while pos + n text.length
i (l, memPrefix) + 1
while i n and pattern[i] text[pos + i]
i++
if i n
pos pos + (i len, memPrefix p 1)
memPrefix 0
else
j l
while j memPrefix and pattern[j] text[pos + j]
j--
if j memPrefix
pos occurences
pos pos p
memPrefix n p
else
q (len, n len) 1
while pos + n text.length
i len + 1
while i n and pattern[i] text[pos i]
i++
if i n
pos pos i len
else
j len
while j 0 and pattern[j] text[pos j]
j--
if j 0
pos occurences
pos pos q
return occurences