<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.25.228.79&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.25.228.79&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/94.25.228.79"/>
		<updated>2026-05-19T22:24:34Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC&amp;diff=71194</id>
		<title>Двусторонний алгоритм</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D1%83%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC&amp;diff=71194"/>
				<updated>2019-05-13T21:07:58Z</updated>
		
		<summary type="html">&lt;p&gt;94.25.228.79: /* Ценность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Двусторонний алгоритм''' (англ. ''Two Way algorithm'') — алгоритм [[Поиск подстроки в строке|поиска подстроки в строке]].&lt;br /&gt;
&lt;br /&gt;
==Характерные черты==&lt;br /&gt;
* Требует упорядоченный алфавит,&lt;br /&gt;
* этап предобработки занимает &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; времени и константное количество памяти,&lt;br /&gt;
* этап поиска за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, где &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; {{---}} длина образца, а &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} длина текста.&lt;br /&gt;
&lt;br /&gt;
==Описание алгоритма==&lt;br /&gt;
Строка &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; разбивается на две части &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; так, что &amp;lt;math&amp;gt;x = uv&amp;lt;/math&amp;gt;. Затем фаза поиска в двустороннем алгоритме состоит в сравнении символов &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; слева направо, и затем, если на первом этапе не происходит несовпадений, в сравнении символов &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; справа налево (второй этап).&lt;br /&gt;
Фаза предобработки, таким образом, заключается в поиске подходящего разбиения &amp;lt;tex&amp;gt;(u, v)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = &amp;lt;tex&amp;gt;(u, v)&amp;lt;/tex&amp;gt; {{---}} '''разбиение''' строки &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;x = uv&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = Пусть &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; {{---}} разбиение &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. '''Повторение''' в &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; {{---}} слово &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; такое, что для него выполнены следующие условия:&lt;br /&gt;
# &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; {{---}} суффикс &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; {{---}} суффикс &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; {{---}} префикс &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; {{---}} префикс &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = Длина повторения в &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; называется '''локальным периодом'''; наименьший локальный период записывается как &amp;lt;math&amp;gt;r(u, v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Каждое разбиение &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; имеет как минимум одно повторение. Очевидно, что &amp;lt;math&amp;gt;1 \leqslant r(u, v) \leqslant |x|&amp;lt;/math&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition = Разбиение &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; такое, что &amp;lt;math&amp;gt;r(u, v) = per(x)&amp;lt;/math&amp;gt; называется '''критическим разбиением''' &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; {{---}} критическое разбиение &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, то на позиции &amp;lt;math&amp;gt;|u|&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; общий и локальный периоды одинаковы. Двусторонний алгоритм находит критическое разбиение &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; такое, что &amp;lt;math&amp;gt;|u| &amp;lt; per(x)&amp;lt;/math&amp;gt; и длина &amp;lt;math&amp;gt;|u|&amp;lt;/math&amp;gt; минимальна.&lt;br /&gt;
Чтобы найти критическое разбиение &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; мы сперва вычислим &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt; {{---}} максимальный суффикс &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; в лексикографическом порядке, характерном для заданного алфавита &amp;lt;math&amp;gt;\leqslant&amp;lt;/math&amp;gt; и максимальный суффикс &amp;lt;math&amp;gt;\widetilde{z}&amp;lt;/math&amp;gt; для обратного лексикографического порядка &amp;lt;math&amp;gt;\geqslant&amp;lt;/math&amp;gt;. Затем &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; выбираются так, что &amp;lt;math&amp;gt;|u| = \max(|z|, |\widetilde{z}|)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Фаза поиска в двустороннем алгоритме состоит из сравнения символов &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; слева направо и символов &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; справа налево. Когда происходит несовпадение при просмотре &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го символа в &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, производится сдвиг длиной &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. Когда происходит несовпадение при просмотре &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; или когда образец встретился в строке, производится сдвиг длиной &amp;lt;math&amp;gt;per(x)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Такие действия приводят к квадратичной работе алгоритма в худшем случае, но этого можно избежать запоминанием префикса: когда производится сдвиг длиной &amp;lt;math&amp;gt;per(x)&amp;lt;/math&amp;gt;, длина совпадающего префикса образца в начале &amp;quot;окна&amp;quot; (а именно &amp;lt;math&amp;gt;m - per(x)&amp;lt;/math&amp;gt;) после сдвига запоминается, чтобы не просматривать ее заново при следующем проходе.&lt;br /&gt;
&lt;br /&gt;
==Псевдокод==&lt;br /&gt;
&lt;br /&gt;
 '''function''' twoWaySearch('''String''' pattern, '''String''' text): '''vector&amp;lt;int&amp;gt;'''&lt;br /&gt;
     &amp;lt;font color=green&amp;gt;//предобработка &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; вычисление критической позиции (в которой строка делится на &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;)&amp;lt;/font&amp;gt;&lt;br /&gt;
     &amp;lt;tex&amp;gt;\langle&amp;lt;/tex&amp;gt;l1, p1&amp;lt;tex&amp;gt;\rangle&amp;lt;/tex&amp;gt; = maxSuffix(pattern, &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\langle&amp;lt;/tex&amp;gt;l2, p2&amp;lt;tex&amp;gt;\rangle&amp;lt;/tex&amp;gt; = maxSuffix(pattern, &amp;lt;tex&amp;gt;\geqslant&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     &amp;lt;tex&amp;gt;\langle&amp;lt;/tex&amp;gt;l, p&amp;lt;tex&amp;gt;\rangle&amp;lt;/tex&amp;gt; = l1 &amp;lt;tex&amp;gt;\geqslant&amp;lt;/tex&amp;gt; l2 ? &amp;lt;tex&amp;gt;\langle&amp;lt;/tex&amp;gt;l1, p1&amp;lt;tex&amp;gt;\rangle&amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;\langle&amp;lt;/tex&amp;gt;l2, p2&amp;lt;tex&amp;gt;\rangle&amp;lt;/tex&amp;gt;&lt;br /&gt;
     &amp;lt;font color=green&amp;gt;//&amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; период &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;-&amp;lt;/tex&amp;gt; такая критическая позиция, что &amp;lt;tex&amp;gt;l&amp;lt;p&amp;lt;/tex&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''vector&amp;lt;int&amp;gt; ''' occurences  &amp;lt;font color=green&amp;gt;// набор всех вхождений образца в текст&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''int''' pos = 0&lt;br /&gt;
     '''int''' memPrefix = 0&lt;br /&gt;
     '''while''' pos + pattern.length &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt; text.length&lt;br /&gt;
     &amp;lt;font color=green&amp;gt;//первый этап: просмотр &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; слева направо&amp;lt;/font&amp;gt;&lt;br /&gt;
         '''int''' i = max(l, memPrefix) + 1&lt;br /&gt;
         '''while''' i &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt; pattern.length '''and''' pattern[i] = text[pos + i]&lt;br /&gt;
             i++&lt;br /&gt;
         '''if''' i &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt; pattern.length&lt;br /&gt;
             pos = pos + max(i - l, memPrefix - p + 1)&lt;br /&gt;
             memPrefix = 0&lt;br /&gt;
         '''else'''&lt;br /&gt;
             &amp;lt;font color=green&amp;gt;//второй этап: просмотр &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; справа налево&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''int''' j = l&lt;br /&gt;
             '''while''' j &amp;lt;tex&amp;gt; &amp;gt; &amp;lt;/tex&amp;gt; memPrefix '''and''' pattern[j] &amp;lt;tex&amp;gt;=&amp;lt;/tex&amp;gt; text[pos + j]&lt;br /&gt;
                 j--&lt;br /&gt;
             '''if''' j &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt; memPrefix&lt;br /&gt;
                 occurences.pushBack(pos)  &lt;br /&gt;
             pos = pos + p&lt;br /&gt;
             memPrefix = pattern.length - p&lt;br /&gt;
     '''return''' occurences&lt;br /&gt;
&lt;br /&gt;
== Ценность алгоритма ==&lt;br /&gt;
Двусторонний алгоритм отчасти похож на алгоритм Бойера-Мура (просмотр символов справа налево и сдвиг позиции при несовпадении на втором этапе), и в лучшем случае работает немногим медленнее его, а в худшем {{---}} значительно превосходит&amp;lt;ref&amp;gt;[http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf Journal of the Association for Computing Machinery, Vol. 38, No, 1, July 1991] Оценки скорости работы&amp;lt;/ref&amp;gt;, но главное отличие двустороннего алгоритма от алгоритмов Кнута-Морриса-Пратта и Бойера-Мура {{---}} константное количество затрачиваемой дополнительной памяти.&lt;br /&gt;
&lt;br /&gt;
Именно этот алгоритм (при выполнении ряда условий) используется в реализации поиска подстроки в glibc&amp;lt;ref&amp;gt;[https://github.com/bminor/glibc/blob/glibc-2.28/string/strstr.c#L88 Реализация функции strstr в glibc]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Алгоритм Кнута-Морриса-Пратта ]]&lt;br /&gt;
* [[Алгоритм Бойера-Мура]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://monge.univ-mlv.fr/~mac/Articles-PDF/CP-1991-jacm.pdf  Оригинал статьи (M. Crochemore, D. Perrin)]&lt;br /&gt;
* [http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260  Краткое описание алгоритма, пример работы]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Поиск подстроки в строке]]&lt;br /&gt;
[[Категория:Точный поиск]]&lt;/div&gt;</summary>
		<author><name>94.25.228.79</name></author>	</entry>

	</feed>