#![allow(dead_code)]
#[allow(clippy::needless_range_loop)]
pub fn edit_distance_lev(a: &str, b: &str) -> usize {
let ac: Vec<char> = a.chars().collect();
let bc: Vec<char> = b.chars().collect();
let m = ac.len();
let n = bc.len();
let mut dp = vec![vec![0usize; n + 1]; m + 1];
for i in 0..=m {
dp[i][0] = i;
}
for j in 0..=n {
dp[0][j] = j;
}
for i in 1..=m {
for j in 1..=n {
let cost = if ac[i - 1] == bc[j - 1] { 0 } else { 1 };
dp[i][j] = (dp[i - 1][j] + 1)
.min(dp[i][j - 1] + 1)
.min(dp[i - 1][j - 1] + cost);
}
}
dp[m][n]
}
pub fn edit_similarity_lev(a: &str, b: &str) -> f32 {
let max_len = a.chars().count().max(b.chars().count());
if max_len == 0 {
return 1.0;
}
let dist = edit_distance_lev(a, b);
1.0 - dist as f32 / max_len as f32
}
pub fn edit_distance_bounded_lev(a: &str, b: &str, max_dist: usize) -> Option<usize> {
let d = edit_distance_lev(a, b);
if d > max_dist {
None
} else {
Some(d)
}
}
pub fn edit_is_close_lev(a: &str, b: &str, threshold: usize) -> bool {
edit_distance_lev(a, b) <= threshold
}
#[allow(clippy::needless_range_loop)]
pub fn longest_common_subsequence_lev(a: &str, b: &str) -> usize {
let ac: Vec<char> = a.chars().collect();
let bc: Vec<char> = b.chars().collect();
let m = ac.len();
let n = bc.len();
let mut dp = vec![vec![0usize; n + 1]; m + 1];
for i in 1..=m {
for j in 1..=n {
if ac[i - 1] == bc[j - 1] {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = dp[i - 1][j].max(dp[i][j - 1]);
}
}
}
dp[m][n]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_edit_distance_equal() {
assert_eq!(edit_distance_lev("hello", "hello"), 0);
}
#[test]
fn test_edit_distance_insert() {
assert_eq!(edit_distance_lev("abc", "abcd"), 1);
}
#[test]
fn test_edit_distance_delete() {
assert_eq!(edit_distance_lev("abcd", "abc"), 1);
}
#[test]
fn test_edit_distance_replace() {
assert_eq!(edit_distance_lev("abc", "axc"), 1);
}
#[test]
fn test_edit_similarity_identical() {
let s = edit_similarity_lev("hello", "hello");
assert!((s - 1.0).abs() < 1e-5);
}
#[test]
fn test_edit_distance_bounded_within() {
assert_eq!(edit_distance_bounded_lev("abc", "axc", 2), Some(1));
assert!(edit_distance_bounded_lev("abc", "xyz", 1).is_none());
}
#[test]
fn test_lcs() {
assert_eq!(longest_common_subsequence_lev("ABCBDAB", "BDCAB"), 4);
}
}