Максимальное независимое множество на деревьях (Independent Set)
Независимое множество: $G = (V, E)$ $IS \subseteq V : \ !\exists u, v \in IS: \ (u,v) \in E$ -- не существует вершин, соединенных ребром.
IS[v] # размер max IS в поддереве с корнем v
IS[l] = 1 # l -- лист
IS[v] = max(sum(IS[w]) + 1, sum(IS[u])) # берем или не берем текущую вершину
# u -- ребенок, w -- внук
Ассимптотика $O(n)$, т.к. каждая вершина 1 раз поучаствует как корень, 1 раз как сын и 1 раз как внук.
Редукционное расстояние (расстояние Левинштейна, EditDistance)
Вход: две строки s1
и s2
Выход: минимальное количество правок, которые переделывают s1
в s2
Правки:
- Добавить букву
- Удалить букву
- Заменить букву
Бывает еще "взвешенная" версия, в которой у каждой операции есть стоимость.
Задача о выравнивании (Alligment)
Вход: две строки s1
и s2
Задача: записать s2
под s1
так, чтобы максимальное число символов совпало (количество различий было минимально).
Пример:
дождь
$\rightarrow$ д-ождь
дрожь
$\rightarrow$ дро-жь
Утверждение: задача о выравнивании для s1
и s2
имеет то же решение, что и edit_distance(s1, s2)
Решение:
e[i, j] = edit_distance(s1[:i+1], s2[:j+1])
e[i, -1] = i # по хорошему надо в основном алгоритме сдвинуть
e[-1, j] = j # индексы, чтобы s[0] давало пустую строку
e[i, j] = min(e[i, j-1] + 1 # + c_add
e[i-1, j] + 1 # + c_remove
e[i-1, j-1] + (1 if s1[i-1] != s2[j-1] else 0) # c_replace
)
Если задача "взвешенная", то единицы заменятся на соответствующие стоимости.
Восстанавливать ответ можно передвигаясь в минимальную ячейку из трех соседей, операция понятна из положения той клетки, куда переходим (диагональ -- замена, горизонталь -- добавление, вертикаль -- удаление).
Замечание: можно решить за $O(|s_1| + |s_2|)$ памяти, если хранить только два столбца. На самом деле за $O(\min{|s_1| + |s_2|})$. Время все равно $O(|s_1|\cdot|s_2|)$. При экономии памяти нет возможности восстановить ответ.
Алгоритм Хиршберга
$O(\min{|s_1| + |s_2|})$ + восстановление ответа.
Лемма: edit_distance(s1, s2) = edit_distance(rev(s1), rev(s2))
, где rev(s) = reversed(s)
Лемма: пусть s2 = s2_1 + s2_2
-- разбиение s2
на две строки.
1. $\exists i:$ ed(s1, s2) = ed(s1[:i], s2_1) + ed(s1[i:], s2_2)
.
2. $\forall i:$ ed(s1, s2) <= ed(s1[:i], s2_1) + ed(s1[i:], s2_2)
Доказательство:
1. Рассмотрим выравнивание s1
и s2
. Если где-нибудь это выравнивание по s2
, то в том же месте можно распилить s1
, и все будет хорошо.
2. Рассмотрим s1
и s2
. Пусть для какого-то j
, по которому разбиваем s2
существует какой-то i
, для которого ed(s1, s2) > ed(s1[:i], s2[:j]) + ed(s1[i:], s2[j:])
. Тогда на основе этих i
и j
можем получить более короткое выравнивание, чем то оптимально, что мы уже получили. Противоречие.
Следствие:
s2 = s2_1 + s2_2
$\Rightarrow \exists i:$ ed(s1, s2) = ed(s1[1:i], s2_1) + ed(s1[n:i+1], rev(s2_2))
Алгоритм:
1. Распилим s2
пополам: s2 = s2_1 + s2_2
.
2. Решаем задачу для s1
и s2_1
3. Решаем задачу для rev(s1)
и s2_2
(решаем старым способом за $O(|s1|)$ памяти)
4. Смотрим на крайние столбцы для двух решений (соседние столбцы в исходной таблице).
Находим min(sum(соотв. ячеек))
(соответствующие ячейки -- диагональные в этих двух столцах) $\Rightarrow$ получаем $i$ из леммы 2
5. Запускаем рекурсивно для (s1[:i]
и s2_1
) и (s1[i:]
и s2_2
)
Ответ можно восстановить по парам индексов i
и j
, запомненных на шаге 4.
Анализ: $T(n) = |s1|\cdot|s2| \frac{|s1|\cdot|s2|}2 + \frac{|s1|\cdot|s2|}4 = O(|s1|\cdot|s2|)$
Такой подход называется Meet in the middle.