Расстояние Левенштейна (редакционное расстояние, дистанция редактирования) – минимальное количество операций удаления, вставки и замены символа, необходимое для преобразования одной строки в другую.
Описание алгоритма
Для поиска расстояния Левенштейна, чаще всего используют алгоритм в котором необходимо заполнить матрицу D, размером n + 1 на m + 1, где n и m – длины сравниваемых строк A и B, по следующим правилам:
- D0,0 = 0
- Di,j = Минимальное из:
- Di-1,j-1 + 0 (символы одинаковые)
- Di,j-1 + wdelete (удаление символа)
- Di-1,j + winsert (вставка символа)
- Di-1,j-1 + wreplace (замена символа)
wdelete, winsert, wreplace – цена или вес удаления, вставки и замены символа.
При этом Dn-1,m-1 – содержит значение расстояния Левенштейна.
Заполнив матрицу для слов SATURDAY и MONDAY получим:
S | A | T | U | R | D | A | Y | ||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
M | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
O | 2 | 2 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
N | 3 | 3 | 3 | 3 | 4 | 5 | 6 | 7 | 8 |
D | 4 | 4 | 4 | 4 | 4 | 5 | 5 | 6 | 7 |
A | 5 | 5 | 4 | 5 | 5 | 5 | 6 | 5 | 6 |
Y | 6 | 6 | 5 | 5 | 6 | 6 | 6 | 6 | 5 |
Реализация алгоритма
Для начала необходимо написать метод для получения минимального из трех чисел. Проще всего это сделать с использованием тернарного оператора:
static int Minimum(int a, int b, int c) => (a = a < b ? a : b) < c ? a : c;
static int Minimum(int a, int b, int c)
{
if(a > b)
{
a = b;
}
if(a > c)
{
a = c;
}
return a;
}
Итеративная реализация
static int LevenshteinDistance(string firstWord, string secondWord)
{
var n = firstWord.Length + 1;
var m = secondWord.Length + 1;
var matrixD = new int[n, m];
const int deletionCost = 1;
const int insertionCost = 1;
for (var i = 0; i < n; i++)
{
matrixD[i, 0] = i;
}
for (var j = 0; j < m; j++)
{
matrixD[0, j] = j;
}
for (var i = 1; i < n; i++)
{
for (var j = 1; j < m; j++)
{
var substitutionCost = firstWord[i - 1] == secondWord[j - 1] ? 0 : 1;
matrixD[i, j] = Minimum(matrixD[i - 1, j] + deletionCost, // удаление
matrixD[i, j - 1] + insertionCost, // вставка
matrixD[i - 1, j - 1] + substitutionCost); // замена
}
}
return matrixD[n - 1, m - 1];
}
Рекурсивная реализация
static int LevenshteinDistance(string text1, int len1, string text2, int len2)
{
if (len1 == 0)
{
return len2;
}
if (len2 == 0)
{
return len1;
}
var substitutionCost = 0;
if(text1[len1 - 1] != text2[len2 - 1])
{
substitutionCost = 1;
}
var deletion = LevenshteinDistance(text1, len1 - 1, text2, len2) + 1;
var insertion = LevenshteinDistance(text1, len1, text2, len2 - 1) + 1;
var substitution = LevenshteinDistance(text1, len1 - 1, text2, len2 - 1) + substitutionCost;
return Minimum(deletion, insertion, substitution);
}
static int LevenshteinDistance(string word1, string word2) =>
LevenshteinDistance(word1, word1.Length, word2, word2.Length);
Программа для поиска расстояния Левенштейна
В класс программы нужно поместить метод для поиска минимального значения и одну из реализаций алгоритма, а в тело метода Main следующий код:
static void Main(string[] args)
{
Console.Write("Первое слово: ");
var s1 = Console.ReadLine();
Console.Write("Второе слово: ");
var s2 = Console.ReadLine();
Console.WriteLine("Расстояние Левенштейна: {0}", LevenshteinDistance(s1, s2));
Console.ReadLine();
}
Результат работы программы: