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