Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
* [Matrix Chain Multiplication](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/matrix_chain_multiplication.r)
* [Minimum Path Sum](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/minimum_path_sum.r)
* [Subset Sum](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/subset_sum.r)
* [Edit Distance](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/edit_distance.r)

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repository already contains a Levenshtein distance implementation (string_manipulation/levenshtein.r, also linked in DIRECTORY.md). Adding a second Levenshtein implementation under Dynamic Programming may be redundant/confusing for users; consider consolidating (extend the existing implementation to optionally return a path) or clearly differentiating this entry (e.g., mention that it returns an operation sequence).

Suggested change
* [Edit Distance](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/edit_distance.r)
* [Edit Distance (Levenshtein, Dynamic Programming)](https://github.com/TheAlgorithms/R/blob/HEAD/dynamic_programming/edit_distance.r)

Copilot uses AI. Check for mistakes.

## Graph Algorithms
* [Bellman Ford Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/bellman_ford_shortest_path.r)
Expand Down
16 changes: 16 additions & 0 deletions documentation/edit_distance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Edit Distance

Levenshtein edit distance calculates the minimum number of single-character insertions, deletions, and substitutions required to transform one string into another.

``` r
source("../dynamic_programming/edit_distance.r")

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

source("../dynamic_programming/edit_distance.r") depends on the working directory being documentation/. If a user runs this from the repo root (common), it will point outside the repository. Consider using a repo-root-relative path (e.g., dynamic_programming/edit_distance.r) or embedding the relevant function code in the doc, consistent with other docs in this folder.

Suggested change
source("../dynamic_programming/edit_distance.r")
source("dynamic_programming/edit_distance.r")

Copilot uses AI. Check for mistakes.

# Compute the edit distance
distance <- edit_distance("kitten", "sitting")
print(distance)

# Reconstruct the optimal sequence of operations
result <- edit_distance_with_path("kitten", "sitting")
print(result$distance)
print(result$operations)
```
105 changes: 105 additions & 0 deletions dynamic_programming/edit_distance.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# edit_distance.r
# Levenshtein edit distance algorithm in R
# Computes the minimum number of insertions, deletions, and substitutions
# required to transform one string into another.
# Time Complexity: O(m * n)
# Space Complexity: O(m * n)

# Compute the Levenshtein distance between two strings
edit_distance <- function(str1, str2) {
#' @param str1: First string
#' @param str2: Second string
#' @return: Integer edit distance
if (!is.character(str1) || !is.character(str2)) {
stop("Both inputs must be character strings.")
}
if (length(str1) != 1 || length(str2) != 1) {
stop("Each input must be a single string.")
}

m <- nchar(str1)
n <- nchar(str2)
dp <- matrix(0L, nrow = m + 1, ncol = n + 1)

# base cases: transform empty prefix
dp[, 1] <- seq(0L, m)
dp[1, ] <- seq(0L, n)

for (i in 2:(m + 1)) {
for (j in 2:(n + 1)) {

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loops for (i in 2:(m + 1)) / for (j in 2:(n + 1)) break for empty strings (e.g., m == 0 makes 2:(m+1) evaluate to c(2, 1)), which can index outside dp and error. Use an empty-safe iteration pattern (e.g., seq_len(m) + 1, seq_len(n) + 1) or early-return when m == 0 or n == 0.

Suggested change
for (i in 2:(m + 1)) {
for (j in 2:(n + 1)) {
for (i in seq_len(m) + 1L) {
for (j in seq_len(n) + 1L) {

Copilot uses AI. Check for mistakes.
cost <- if (substr(str1, i - 1, i - 1) == substr(str2, j - 1, j - 1)) 0L else 1L
dp[i, j] <- min(
dp[i - 1, j] + 1L, # deletion
dp[i, j - 1] + 1L, # insertion
dp[i - 1, j - 1] + cost # substitution
)
}
}

return(dp[m + 1, n + 1])
}

# Compute the edit distance and reconstruct an optimal alignment path
edit_distance_with_path <- function(str1, str2) {
#' @param str1: First string
#' @param str2: Second string
#' @return: List with distance, operations, and dp table
if (!is.character(str1) || !is.character(str2)) {
stop("Both inputs must be character strings.")
}
if (length(str1) != 1 || length(str2) != 1) {
stop("Each input must be a single string.")
}

m <- nchar(str1)
n <- nchar(str2)
dp <- matrix(0L, nrow = m + 1, ncol = n + 1)
dp[, 1] <- seq(0L, m)
dp[1, ] <- seq(0L, n)

for (i in 2:(m + 1)) {
for (j in 2:(n + 1)) {
cost <- if (substr(str1, i - 1, i - 1) == substr(str2, j - 1, j - 1)) 0L else 1L
dp[i, j] <- min(
Comment on lines +54 to +63

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same empty-string issue as above: for (i in 2:(m + 1)) / for (j in 2:(n + 1)) can iterate invalid indices when m == 0 or n == 0, leading to out-of-bounds dp access. Please make the iteration empty-safe or handle m == 0 / n == 0 up front (and still produce a valid operations list).

Copilot uses AI. Check for mistakes.
dp[i - 1, j] + 1L,
dp[i, j - 1] + 1L,
dp[i - 1, j - 1] + cost
)
}
}

i <- m + 1
j <- n + 1
ops <- character()

while (i > 1 || j > 1) {
if (i > 1 && j > 1 && dp[i, j] == dp[i - 1, j - 1] +
(substr(str1, i - 1, i - 1) != substr(str2, j - 1, j - 1))) {
if (substr(str1, i - 1, i - 1) == substr(str2, j - 1, j - 1)) {
ops <- c("match", ops)
} else {
ops <- c(sprintf("substitute '%s' -> '%s'", substr(str1, i - 1, i - 1), substr(str2, j - 1, j - 1)), ops)
}
i <- i - 1
j <- j - 1
} else if (i > 1 && dp[i, j] == dp[i - 1, j] + 1L) {
ops <- c(sprintf("delete '%s'", substr(str1, i - 1, i - 1)), ops)
i <- i - 1
} else {
ops <- c(sprintf("insert '%s'", substr(str2, j - 1, j - 1)), ops)
j <- j - 1
}
}

return(list(
distance = dp[m + 1, n + 1],
operations = ops,
dp_table = dp
))
}

# Example usage:
# print(edit_distance("kitten", "sitting"))
# result <- edit_distance_with_path("kitten", "sitting")
# print(result$distance)
# print(result$operations)
Loading