9.1 KiB
Задача о редакционном расстоянии
Редакционное расстояние, также называемое расстоянием Левенштейна, указывает минимальное количество изменений, необходимых для взаимного преобразования двух строк, и обычно используется в информационном поиске и обработке естественного языка для измерения сходства двух последовательностей.
!!! question
Даны две строки $s$ и $t$, верните минимальное количество шагов редактирования, необходимых для преобразования $s$ в $t$.
Вы можете выполнять три операции редактирования в строке: вставить символ, удалить символ, заменить символ на любой другой символ.
Как показано на рисунке ниже, для преобразования kitten в sitting требуется 3 шага редактирования, включая 2 операции замены и 1 операцию добавления; для преобразования hello в algo требуется 3 шага, включая 2 операции замены и 1 операцию удаления.
Задачу о редакционном расстоянии можно естественным образом объяснить с помощью модели дерева решений. Строки соответствуют узлам дерева, один раунд решения (одна операция редактирования) соответствует ребру дерева.
Как показано на рисунке ниже, без ограничения операций каждый узел может породить множество ребер, каждое ребро соответствует одной операции, что означает, что существует множество возможных путей преобразования от hello к algo.
С точки зрения дерева решений цель этой задачи -- найти кратчайший путь между узлом hello и узлом algo.
Подход динамического программирования
Первый шаг: обдумать решение на каждом раунде, определить состояние, чтобы получить таблицу $dp$
Решение на каждом раунде -- это выполнение одной операции редактирования над строкой s.
Мы хотим, чтобы в процессе операций редактирования размер задачи постепенно уменьшался, чтобы можно было построить подзадачи. Пусть длины строк s и t равны n и m соответственно, сначала рассмотрим символы в конце обеих строк s[n-1] и t[m-1].
- Если
s[n-1]иt[m-1]одинаковы, мы можем пропустить их и сразу рассмотретьs[n-2]иt[m-2]. - Если
s[n-1]иt[m-1]различны, нам нужно выполнить одно редактированиеs(вставка, удаление, замена), чтобы символы в конце обеих строк совпали, после чего можно пропустить их и рассмотреть задачу меньшего размера.
Другими словами, каждое решение (операция редактирования), которое мы принимаем в строке s, приводит к изменению оставшихся несопоставленных символов в s и t. Следовательно, состояние -- это текущие рассматриваемые $i$-й и $j$-й символы в s и t, обозначаемые как [i, j].
Состояние [i, j] соответствует подзадаче: минимальное количество шагов редактирования, необходимых для изменения первых i символов s на первые j символов $t$.
Таким образом, получаем двумерную таблицу dp размером (i+1) \times (j+1).
Второй шаг: найти оптимальную подструктуру, затем вывести уравнение перехода состояния
Рассмотрим подзадачу dp[i, j], символы в конце соответствующих двух строк -- это s[i-1] и t[j-1], можно разделить на три случая, показанные на рисунке ниже, в зависимости от различных операций редактирования.
- Добавить
t[j-1]послеs[i-1], тогда остается подзадачаdp[i, j-1]. - Удалить
s[i-1], тогда остается подзадачаdp[i-1, j]. - Заменить
s[i-1]наt[j-1], тогда остается подзадачаdp[i-1, j-1].
На основе приведенного выше анализа можно получить оптимальную подструктуру: минимальное количество шагов редактирования dp[i, j] равно минимальному количеству шагов редактирования среди dp[i, j-1], dp[i-1, j], dp[i-1, j-1], плюс текущий шаг редактирования 1. Соответствующее уравнение перехода состояния:
dp[i, j] = \min(dp[i, j-1], dp[i-1, j], dp[i-1, j-1]) + 1
Обратите внимание, когда s[i-1] и t[j-1] одинаковы, не требуется редактировать текущий символ, в этом случае уравнение перехода состояния:
dp[i, j] = dp[i-1, j-1]
Третий шаг: определить граничные условия и порядок перехода состояния
Когда обе строки пусты, количество шагов редактирования равно 0, то есть dp[0, 0] = 0. Когда s пуста, но t не пуста, минимальное количество шагов редактирования равно длине t, то есть первая строка dp[0, j] = j. Когда s не пуста, но t пуста, минимальное количество шагов редактирования равно длине s, то есть первый столбец dp[i, 0] = i.
Наблюдая за уравнением перехода состояния, решение dp[i, j] зависит от решений слева, сверху и слева-сверху, поэтому можно обойти всю таблицу dp в прямом порядке с помощью двух циклов.
Реализация кода
[file]{edit_distance}-[class]{}-[func]{edit_distance_dp}
Как показано на рисунке ниже, процесс перехода состояния задачи о редакционном расстоянии очень похож на задачу о рюкзаке, оба можно рассматривать как процесс заполнения двумерной сетки.











