<?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=109.194.220.98&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=109.194.220.98&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/109.194.220.98"/>
		<updated>2026-06-11T14:25:43Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%92%D0%B0%D0%B3%D0%BD%D0%B5%D1%80%D0%B0-%D0%A4%D0%B8%D1%88%D0%B5%D1%80%D0%B0&amp;diff=73345</id>
		<title>Задача о редакционном расстоянии, алгоритм Вагнера-Фишера</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%92%D0%B0%D0%B3%D0%BD%D0%B5%D1%80%D0%B0-%D0%A4%D0%B8%D1%88%D0%B5%D1%80%D0%B0&amp;diff=73345"/>
				<updated>2020-03-26T14:11:26Z</updated>
		
		<summary type="html">&lt;p&gt;109.194.220.98: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|id=levenstain_dist&lt;br /&gt;
|definition=&lt;br /&gt;
'''Расстояние Левенштейна''' (англ. ''Levenshtein distance'') (также '''редакционное расстояние''' или '''дистанция редактирования''') между двумя строками в теории информации и компьютерной лингвистике — это минимальное количество операций вставки одного символа, удаления одного символа и замены одного символа на другой, необходимых для превращения одной строки в другую.}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
Для расстояния Левенштейна справедливы следующие утверждения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \geqslant | |S_1| - |S_2| |&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \leqslant max( |S_1| , |S_2| )&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = 0 \Leftrightarrow S_1 = S_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2)&amp;lt;/tex&amp;gt; — расстояние Левенштейна между строками &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;|S|&amp;lt;/tex&amp;gt; — длина строки &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Расстояние Левенштейна является [[Метрическое пространство|метрикой]].&lt;br /&gt;
Для того чтобы доказать это достаточно доказать что выполняется неравенство треугольника:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_3) \leqslant \rm{d}(S_1,S_2) + \rm{d}(S_2,S_3)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\rm{d}(S_1,S_3) = x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = y&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\rm{d}(S_2,S_3) = z&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; — кратчайшее редакционное расстояние от &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;S_3&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — кратчайшее редакционное расстояние от &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; — кратчайшее редакционное расстояние от &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;S_3&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;y + z&amp;lt;/tex&amp;gt; — какое-то расстояние от &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;S_3&amp;lt;/tex&amp;gt;. В других случаях &amp;lt;tex&amp;gt;\rm{d}(S_1,S_3) &amp;lt; \rm{d}(S_1,S_2) + \rm{d}(S_2,S_3)&amp;lt;/tex&amp;gt;. Следовательно, выполняется неравенство треугольника.&lt;br /&gt;
&lt;br /&gt;
== Разные цены операций ==&lt;br /&gt;
&lt;br /&gt;
Цены операций могут зависеть от вида операции (вставка, удаление, замена) и/или от участвующих в ней символов, отражая разную вероятность разных ошибок при вводе текста, и т. п. В общем случае:&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(a, b)&amp;lt;/tex&amp;gt; — цена замены символа &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; на символ &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(\varepsilon, b)&amp;lt;/tex&amp;gt; — цена вставки символа &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(a, \varepsilon)&amp;lt;/tex&amp;gt; — цена удаления символа &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для решения задачи о редакционном расстоянии необходимо найти последовательность замен, минимизирующую суммарную цену. Расстояние Левенштейна является частным случаем этой задачи при&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(a, a) = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(a, b) = 1&amp;lt;/tex&amp;gt; при &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(\varepsilon, b) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;w(a, \varepsilon) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\varepsilon&amp;lt;/tex&amp;gt; — пустая последовательность.&lt;br /&gt;
&lt;br /&gt;
Как частный случай, так и задачу для произвольных &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, решает алгоритм Вагнера-Фишера, приведённый ниже. Здесь и ниже мы считаем, что все &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; неотрицательны, и действует правило треугольника: если две последовательные операции можно заменить одной, это не ухудшает общую цену (например, заменить символ &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt;, а потом с &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt; не лучше, чем сразу &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Формула ==&lt;br /&gt;
&lt;br /&gt;
Будем считать, что элементы строк нумеруются с первого, как принято в математике, а не нулевого.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — две строки (длиной &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; соответственно) над некоторым алфавитом, тогда редакционное расстояние &amp;lt;tex&amp;gt;\rm{d}(S_1, S_2)&amp;lt;/tex&amp;gt; можно подсчитать по следующей рекуррентной формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\ \rm{d}(S_1, S_2) = \rm{D}(M,N)&amp;lt;/tex&amp;gt; , где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
D(i, j) = \left\{\begin{array}{llcl}&lt;br /&gt;
0&amp;amp;;\ i = 0,\ j = 0\\&lt;br /&gt;
i * deleteCost&amp;amp;;\ j = 0,\ i &amp;gt; 0\\&lt;br /&gt;
j * insertCost&amp;amp;;\ i = 0,\ j &amp;gt; 0\\&lt;br /&gt;
D(i - 1, j - 1)&amp;amp;;\ S_1[i] = S_2[j]\\&lt;br /&gt;
\min{(}\\&lt;br /&gt;
\begin{array}{llcl}&lt;br /&gt;
&amp;amp;D(i, j - 1) + insertCost\\&lt;br /&gt;
&amp;amp;D(i - 1, j) + deleteCost&amp;amp;&amp;amp;\\&lt;br /&gt;
&amp;amp;D(i - 1, j - 1) + replaceCost\\&lt;br /&gt;
\end{array}&amp;amp;;\ j &amp;gt; 0,\ i &amp;gt; 0,\ S_1[i] \ne S_2[j]\\&lt;br /&gt;
)&lt;br /&gt;
\end{array}\right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\min(a, b, c)&amp;lt;/tex&amp;gt; возвращает наименьший из аргументов.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим формулу более подробно. Здесь &amp;lt;tex&amp;gt;D(i, j)&amp;lt;/tex&amp;gt; — расстояние между префиксами строк: первыми &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; символами строки &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и первыми &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; символами строки &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Для нашей динамики должен выполняться [[Динамическое программирование|принцип оптимальности на префиксе]]. Очевидно, что редакционное расстояние между двумя пустыми строками равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, нужно совершить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; операций удаления, а чтобы получить строку длиной &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; из пустой, нужно произвести &amp;lt;tex&amp;gt;j&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;tex&amp;gt;y&amp;lt;/tex&amp;gt;, потом &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;, выгоднее было сразу заменить &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;).&lt;br /&gt;
* Две замены разных символов можно менять местами&lt;br /&gt;
* Два стирания или две вставки можно менять местами&lt;br /&gt;
* Вставка символа с его последующим стиранием — неоптимально (можно их обе отменить)&lt;br /&gt;
* Стирание и вставку разных символов можно менять местами&lt;br /&gt;
* Вставка символа с его последующей заменой — неоптимально (излишняя замена)&lt;br /&gt;
* Вставка символа и замена другого символа меняются местами&lt;br /&gt;
* Замена символа с его последующим стиранием — неоптимально (излишняя замена)&lt;br /&gt;
* Стирание символа и замена другого символа меняются местами&lt;br /&gt;
&lt;br /&gt;
Пускай &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; кончается на символ &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; кончается на символ &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Есть три варианта:&lt;br /&gt;
# Символ «а», на который кончается &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, после чего превратили первые &amp;lt;tex&amp;gt;i-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (на что потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)&amp;lt;/tex&amp;gt; операций), значит, всего потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)+1&amp;lt;/tex&amp;gt; операций&lt;br /&gt;
# Символ &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, на который кончается &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в первые &amp;lt;tex&amp;gt;j-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, после чего добавили &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Аналогично предыдущему случаю, потребовалось &amp;lt;tex&amp;gt;D(i,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
# Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, то чтобы сделать последним символом &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой неоптимально). Значит, символов справа от финального &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; мы не добавляли. Самого финального &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа — его замена. Заменять его 2 или больше раз неоптимально. Значит,&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a=b&amp;lt;/tex&amp;gt;, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций, значит, всего потребуется &amp;lt;tex&amp;gt;D(i-1,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Вагнера — Фишера ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения кратчайшего расстояния необходимо вычислить матрицу &amp;lt;tex&amp;gt;D&amp;lt;/tex&amp;gt;, используя [[#Формула|вышеприведённую формулу]]. Её можно вычислять как по строкам, так и по столбцам. &lt;br /&gt;
Псевдокод алгоритма, написанный при произвольных ценах замен, вставок и удалений (важно помнить, что элементы нумеруются с &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
Псевдокод ниже решает простой частный случай, когда вставка символа, удаление символа и замена одного символа на другой стоят одинаково для любых символов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''int''' '''levensteinInstruction'''('''String''' s1, '''String''' s2, '''int''' InsertCost, '''int''' DeleteCost, '''int''' ReplaceCost):&lt;br /&gt;
   D[0][0] = 0&lt;br /&gt;
   '''fo'''r j = 1 '''to''' N&lt;br /&gt;
     D[0][j] = D[0][j - 1] + InsertCost                  &lt;br /&gt;
   '''for''' i = 1 '''to''' M&lt;br /&gt;
     D[i][0] = D[i - 1][0] + DeleteCost                  &lt;br /&gt;
     '''for''' j = 1 '''to''' N&lt;br /&gt;
       '''if''' S1[i] != S2[j] &lt;br /&gt;
          D[i][j] = min(D[i - 1][j] + DeleteCost,        &lt;br /&gt;
                        D[i][j - 1] + InsertCost,                      &lt;br /&gt;
                        D[i - 1][j - 1] + ReplaceCost)                 &lt;br /&gt;
       '''else''' &lt;br /&gt;
          D[i][j] = D[i - 1][j - 1]&lt;br /&gt;
   '''return''' D[M][N]&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Память ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм в виде, описанном выше, требует &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; операций и такую же память, однако, если требуется только расстояние, легко уменьшить требуемую память до &amp;lt;tex&amp;gt;\Theta(N)&amp;lt;/tex&amp;gt;. Заметим, что для вычисления &amp;lt;tex&amp;gt;D[i]&amp;lt;/tex&amp;gt; нам нужно только &amp;lt;tex&amp;gt;D[i - 1]&amp;lt;/tex&amp;gt;, поэтому будем вычислять &amp;lt;tex&amp;gt;D[i]&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;D[1]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;D[i - 1]&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;D[0]&amp;lt;/tex&amp;gt;. Осталось только поменять местами &amp;lt;tex&amp;gt;D[1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;D[0]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''int''' '''levensteinInstruction'''('''int[]''' D):&lt;br /&gt;
   '''for''' i = 0 '''to''' M&lt;br /&gt;
     '''for''' j = 0 '''to''' N&lt;br /&gt;
       вычислить D[1][j]&lt;br /&gt;
     swap(D[0], D[1])&lt;br /&gt;
   '''return''' D[0][N]&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Рекурсивный алгоритм ==&lt;br /&gt;
Для того, чтобы обеспечить время &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; при памяти &amp;lt;tex&amp;gt;\Theta(\min(M,N))&amp;lt;/tex&amp;gt;, определим матрицу &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; минимальных расстояний между ''суффиксами'' строк, то есть &amp;lt;tex&amp;gt; E(i, j) &amp;lt;/tex&amp;gt; {{---}} расстояние между последними &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; символами &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и последними &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; символами &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Очевидно, матрицу &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; можно вычислить аналогично матрице &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt;, и так же быстро.&lt;br /&gt;
&lt;br /&gt;
Теперь опишем алгоритм, считая, что &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — кратчайшая из двух строк.&lt;br /&gt;
&lt;br /&gt;
* Если длина одной из строк (или обеих) не больше &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;, задача тривиальна. Если нет, выполним следующие шаги.&lt;br /&gt;
* Разделим строку &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; на две подстроки длиной &amp;lt;tex&amp;gt;M/2&amp;lt;/tex&amp;gt;. (Если &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; нечётно, то длины подстрок будут &amp;lt;tex&amp;gt;(M-1)/2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(M+1)/2&amp;lt;/tex&amp;gt;.) Обозначим подстроки &amp;lt;tex&amp;gt;S_1^-&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_1^+&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Для вычислим последнюю строку матрицы &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; для строк &amp;lt;tex&amp;gt;S_1^-&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, последнюю строку матрицы &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; для строк &amp;lt;tex&amp;gt;S_1^+&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Найдём &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; такое, что &amp;lt;tex&amp;gt;D(|S_1^-|, i) + E(|S_1^+|, N-i)&amp;lt;/tex&amp;gt; минимально. Здесь &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; E &amp;lt;/tex&amp;gt; {{---}} матрицы из предыдущего шага, но мы используем только их последние строки. Таким образом, мы нашли разбиение &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; на две подстроки, минимизирующее сумму расстояния левой половины &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; до левой части &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; и расстояния правой половины &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; до правой части &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Следовательно, левая подстрока &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; соответствует левой половине &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, а правая {{---}} правой.&lt;br /&gt;
* Рекурсивно ищем редакционное предписание, превращающее &amp;lt;tex&amp;gt;S_1^-&amp;lt;/tex&amp;gt; в левую часть &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (то есть в подстроку &amp;lt;tex&amp;gt;S_2[1...i]&amp;lt;/tex&amp;gt;)&lt;br /&gt;
* Рекурсивно ищем редакционное предписание, превращающее &amp;lt;tex&amp;gt;S_1^+&amp;lt;/tex&amp;gt; в правую часть &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (то есть в подстроку &amp;lt;tex&amp;gt;S_2[i+1...N]&amp;lt;/tex&amp;gt;).&lt;br /&gt;
* Объединяем оба редакционных предписания.&lt;br /&gt;
&lt;br /&gt;
===Псевдокод===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''int''' '''levensteinInstruction'''('''String''' s1, '''String''' s2):&lt;br /&gt;
    '''if'''  s1.length &amp;lt;=  1 || s2.length &amp;lt;= 1  &lt;br /&gt;
        Решаем тривиально, возвращаем редакционное предписание&lt;br /&gt;
    '''else'''    &lt;br /&gt;
        String s1l, s1r, s2l, s2r&lt;br /&gt;
        '''if'''  s2.length &amp;lt; s1.length &lt;br /&gt;
            s1l = s1.substring(0, s1.length / 2)           &amp;lt;font color=darkgreen&amp;gt; // S1-&amp;lt;/font color=darkgreen&amp;gt; &lt;br /&gt;
            s1r = s1.substring(s1.length / 2, s1.length)   &amp;lt;font color=darkgreen&amp;gt; // S1+&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
                                                           &amp;lt;font color=darkgreen&amp;gt; // d, e - массивы&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            d = '''calcD'''(s1l, s2)                             &amp;lt;font color=darkgreen&amp;gt; // Вычисляем последнюю строку матрицы D для S1- и S2&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            e = '''calcE'''(s1r, s2)                             &amp;lt;font color=darkgreen&amp;gt; // Вычисляем последнюю строку матрицы E для S1+ и S2&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            k = 0&lt;br /&gt;
            '''for''' i = 1 '''to''' s2.length&lt;br /&gt;
                '''if''' d[i] + e[s2.length - i] &amp;lt; d[k] + e[s2.length - k]&lt;br /&gt;
                    k = i&lt;br /&gt;
            s2l = s2.substring(0, k)&lt;br /&gt;
            s2r = s2.substring(k, s2.length)&lt;br /&gt;
        '''else'''&lt;br /&gt;
                                                           &amp;lt;font color=darkgreen&amp;gt; // s1 - меньшая строка&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            s2l = s2.substring(0, s2.length / 2)           &amp;lt;font color=darkgreen&amp;gt; // S2-&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            s2r = s2.substring(s2.length / 2, s2.length)   &amp;lt;font color=darkgreen&amp;gt; // S2+&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            d = '''calcD'''(s2l, s1)                             &amp;lt;font color=darkgreen&amp;gt; // Вычисляем последнюю строку матрицы D для S2- и S1&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            e = '''calcE'''(s2r, s1)                             &amp;lt;font color=darkgreen&amp;gt; // Вычисляем последнюю строку матрицы E для S2+ и S1&amp;lt;/font color=darkgreen&amp;gt;&lt;br /&gt;
            k = 0&lt;br /&gt;
            '''for''' i = 1 '''to''' s1.length&lt;br /&gt;
                '''if''' d[i] + e[s1.length - i] &amp;lt; d[k] + e[s1.length - k]&lt;br /&gt;
                    k = i&lt;br /&gt;
            s1l = s1.substring(0, k)&lt;br /&gt;
            s1r = s1.substring(k, s1.length)&lt;br /&gt;
    '''return''' '''levensteinInstruction'''(s1l, s2l) + '''levensteinInstruction'''(s1r, s2r)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время выполнения удовлетворяет (с точностью до умножения на константу) условию&lt;br /&gt;
: &amp;lt;tex&amp;gt;T(M,N)=MN+T(M/2,N')+T(M/2,N-N'),\ 0\leqslant N'\leqslant N&amp;lt;/tex&amp;gt;,&lt;br /&gt;
Докажем:&lt;br /&gt;
: &amp;lt;tex&amp;gt;T(M,N) \leqslant 2MN&amp;lt;/tex&amp;gt;&lt;br /&gt;
База индукции очевидна&lt;br /&gt;
: &amp;lt;tex&amp;gt;T(1,N) = N \leqslant 2N&amp;lt;/tex&amp;gt;&lt;br /&gt;
Пусть для всех &amp;lt;tex&amp;gt;M' &amp;lt; M&amp;lt;/tex&amp;gt; выполнено &amp;lt;tex&amp;gt;T(M',N') \leqslant 2M'N'&amp;lt;/tex&amp;gt;.  &lt;br /&gt;
Тогда учитывая &amp;lt;tex&amp;gt;T(M/2,N') \leqslant 2(M/2)N'&amp;lt;/tex&amp;gt;,  &amp;lt;tex&amp;gt;T(M/2,N-N') \leqslant 2(M/2)(N-N')&amp;lt;/tex&amp;gt;, получим:&lt;br /&gt;
: &amp;lt;tex&amp;gt;T(M,N)=MN+T(M/2,N')+T(M/2,N-N') \leqslant&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; MN+2(M/2)N'+2(M/2)(N-N')=2MN&amp;lt;/tex&amp;gt;&lt;br /&gt;
следовательно&lt;br /&gt;
: &amp;lt;tex&amp;gt;T(M,N) = \Theta(M \cdot N)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для вычисления последних строк матриц &amp;lt;tex&amp;gt; D, ~E &amp;lt;/tex&amp;gt; можно использовать два глобальных двумерных массива размера &amp;lt;tex&amp;gt;2 \times (\min(M, N)+1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Т.к. мы вычисляем функцию рекурсивно, требуемый размер стека тоже следует учесть. На стек вызовов потребуется &amp;lt;tex&amp;gt;\Theta(\log(\max(M,N))&amp;lt;/tex&amp;gt; памяти, потому общая оценка использования памяти будет &amp;lt;tex&amp;gt; \Theta(\min(M,N)) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Редакционное предписание ==&lt;br /&gt;
&lt;br /&gt;
'''Редакционное предписание''' (англ. ''Editorial prescription'') — последовательность действий, необходимых для получения из первой строки второй кратчайшим образом. Обычно действия обозначаются так: '''D''' (англ. delete) — удалить, '''I''' (англ. insert) — вставить, '''R''' (англ. replace) — заменить, '''M''' (англ. match) — совпадение.&lt;br /&gt;
&lt;br /&gt;
Например, для 2-х строк «hell123» и «hello214» можно построить следующую таблицу преобразований:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!'''M''' ||'''M''' ||'''M''' ||'''M''' ||'''R''' ||'''M''' ||'''R''' ||'''I'''&lt;br /&gt;
|-&lt;br /&gt;
|h ||e ||l ||l ||1 ||2||3 ||&lt;br /&gt;
|-&lt;br /&gt;
|h ||e||l ||l ||o ||2 ||1 ||4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
*[[Задача о расстоянии Дамерау-Левенштейна]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://en.wikipedia.org/wiki/Levenshtein_distance Wikipedia {{---}} Levenshtein distance]&lt;br /&gt;
&lt;br /&gt;
*[http://pastie.org/5574488 Реализация рекурсивного алгоритма поиска редакционного предписания на языке Java]&lt;br /&gt;
&lt;br /&gt;
*Романовский И.В. &amp;quot;Дискретный анализ&amp;quot;. Третье издание. Стр. 103 - 105&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>109.194.220.98</name></author>	</entry>

	</feed>