From 6ce99e49c4392ed4a6ff692eb27005a0f69504e5 Mon Sep 17 00:00:00 2001 From: Yuri Filgueira Date: Thu, 30 May 2024 14:36:58 -0300 Subject: [PATCH] Feat: Levenshtein distance algorithm --- .../strings/LevenshteinDistance.java | 57 +++++++++++++++++++ .../strings/LevenshteinDistanceTest.java | 51 +++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/main/java/com/williamfiset/algorithms/strings/LevenshteinDistance.java create mode 100644 src/test/java/com/williamfiset/algorithms/strings/LevenshteinDistanceTest.java diff --git a/src/main/java/com/williamfiset/algorithms/strings/LevenshteinDistance.java b/src/main/java/com/williamfiset/algorithms/strings/LevenshteinDistance.java new file mode 100644 index 000000000..081c1613a --- /dev/null +++ b/src/main/java/com/williamfiset/algorithms/strings/LevenshteinDistance.java @@ -0,0 +1,57 @@ +package com.williamfiset.algorithms.strings; + +public class LevenshteinDistance { + + // The Levenshtein distance algorithm calculates the minimum number of edits + // (insertions, deletions, or substitutions) needed to transform one string + // into another. It utilizes a matrix to store the distances between all + // prefixes of the two strings, and iteratively fills this matrix using + // dynamic programming. The final value in the matrix represents the + // Levenshtein distance between the two original strings. + public int calculateDistance(String origin, String destiny) { + int rows = origin.length(); + int columns = destiny.length(); + + if (rows == 0 && columns == 0) { + return 0; + } else if (rows == 0) { + return columns; + } else if (columns == 0) { + return rows; + } + + int[][] distance = new int[rows + 1][columns + 1]; + + for (int i = 0; i <= rows; i++) { + distance[i][0] = i; + } + + for (int i = 0; i <= columns; i++) { + distance[0][i] = i; + } + + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= columns; j++) { + if (origin.charAt(i - 1) == destiny.charAt(j - 1)) { + distance[i][j] = distance[i - 1][j - 1]; + } else { + distance[i][j] = + 1 + + Math.min( + distance[i][j - 1], Math.min(distance[i - 1][j], distance[i - 1][j - 1])); + } + } + } + + return distance[rows][columns]; + } + + public static void main(String[] args) { + LevenshteinDistance levenshteinDistance = new LevenshteinDistance(); + + String origin = "Rust is a good language"; + String destiny = "Java is a good language"; + + System.out.println(levenshteinDistance.calculateDistance(origin, destiny)); + } +} diff --git a/src/test/java/com/williamfiset/algorithms/strings/LevenshteinDistanceTest.java b/src/test/java/com/williamfiset/algorithms/strings/LevenshteinDistanceTest.java new file mode 100644 index 000000000..7b24fda96 --- /dev/null +++ b/src/test/java/com/williamfiset/algorithms/strings/LevenshteinDistanceTest.java @@ -0,0 +1,51 @@ +package com.williamfiset.algorithms.strings; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LevenshteinDistanceTest { + + private LevenshteinDistance levenshteinDistance; + + @BeforeEach + public void setUp() { + levenshteinDistance = new LevenshteinDistance(); + } + + @Test + public void calculateDistanceTest() { + assertEquals(2, levenshteinDistance.calculateDistance("Java", "javac")); + assertEquals(1, levenshteinDistance.calculateDistance("Rust", "rust")); + assertEquals(0, levenshteinDistance.calculateDistance("Spring", "Spring")); + assertEquals(0, levenshteinDistance.calculateDistance("Rest API", "Rest API")); + assertEquals(3, levenshteinDistance.calculateDistance("Rest API", "Rest api")); + assertEquals( + 416, + levenshteinDistance.calculateDistance( + "gPfjkLwbvCdRmnXiQhZtyuEsWvaYozLqBMneRxPqOmgLjdSkFzuVrCtXpoKmBvJnzTiSqWmxOtPqlReVdSkiUmW" + + "vtXyNpsJfZoElQuRhKjGnLbZtYuVmAsPnRjMiQxOeLsKmWpTvZcQdNfHbRvTyWlXpMjOoKbZvFnTrXsPnMdJlQyUwHrKjGmAzXoVoPqTiS" + + "fYzUcNdLgKjBvQrXySnJtZlMpFqWvGsNzIrYoXtPuKdVcQhWpYlRnMgJsKqOtPiYxZrCwFvShNtLkMoQjTxUbWrXpVzFmJrGuSnOpKzJyR" + + "mQhPzTlWvBqMvJkKsLnGrXyFtNkZoPqJmEyWsFuCzHrRkTuLbJoQvNfGtYwVrYsXzPqLeVjTdKcWoMnZbRfHuJpXqNsMiVvZoFtKlDjUyN" + + "rVwYtPsBoLgKvZdHrLqUmXpNzJwOvFyRdHtCsKxVmPoJzWbQvShLpNr", + "nWjPuQfRkTgLxMbVhZySdIcKnOwXoEqVzCjBpSlDrUmAtYvJzWgNxQmUoErCsPlRfTgLbVwXtMjQhXvNsMpKzDrYcVtWlBzJsPkQnUvHaZr" + + "LdNiVwQmGfXoUeArSkQmWtHpVrLyQkNwTrXsUzDcKoJmLvHpRkYoQwUsFiXbMjGtVkPnWoUsXvZfRqOiTnLrGmYqKsPhXwDbTjLgVkQpNj" + + "WxFbErVhMuCnXoWpVjLbZqTdWmHpZkUfQiSnJrXaZtKpLwGrOzWyLsUmQjNvGvFbXuLqPsNrMnXtViQkVpOySqMuKtPcNrRzLhJxWoVfQrX" + + "lGrQyKdXnJiMtPsBuLoGfWzTdVjIrCoZlHsXpNmVpKrOhRfXwGpWvBsKjOyTvDbMiVjTuYpQrKnYuLbPzFhUpQsMlJtWsNrVoPjXnKzRdIkNxSfOlRpKrUhPq")); + } + + public @Test void calculateDistanceWithEmptyOriginTest() { + assertEquals(9, levenshteinDistance.calculateDistance("", "Algorithm")); + } + + @Test + public void calculateDistanceWithEmptyDestinyTest() { + assertEquals(9, levenshteinDistance.calculateDistance("Algorithm", "")); + } + + @Test + public void calculateDistanceWithTwoEmptyStringsTest() { + assertEquals(0, levenshteinDistance.calculateDistance("", "")); + } +}