1pub(crate) trait EditDistance {
3 fn edit_distance(&self, other: &Self) -> usize;
5}
6
7impl EditDistance for &[u8] {
8 #[expect(clippy::arithmetic_side_effects, clippy::indexing_slicing)]
11 fn edit_distance(&self, other: &Self) -> usize {
12 let mut dp = vec![vec![0; other.len() + 1]; self.len() + 1];
13
14 for i in 0..=self.len() {
15 for j in 0..=other.len() {
16 if i == 0 {
17 dp[i][j] = j;
18 } else if j == 0 {
19 dp[i][j] = i;
20 } else if self[i - 1] == other[j - 1] {
21 dp[i][j] = dp[i - 1][j - 1];
22 } else {
23 dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]);
24 }
25 }
26 }
27
28 dp[self.len()][other.len()]
29 }
30}
31
32impl EditDistance for String {
33 fn edit_distance(&self, other: &Self) -> usize {
37 self.as_bytes().edit_distance(&other.as_bytes())
38 }
39}
40
41impl EditDistance for &str {
42 fn edit_distance(&self, other: &Self) -> usize {
46 self.as_bytes().edit_distance(&other.as_bytes())
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use rstest::rstest;
53
54 use super::*;
55
56 #[rstest]
57 #[case("kitten", "sitting", 3)]
58 #[case("flaw", "lawn", 2)]
59 #[case("intention", "execution", 5)]
60 #[case("", "", 0)]
61 #[case("a", "", 1)]
62 #[case("", "a", 1)]
63 fn test_edit_distance(#[case] s1: &str, #[case] s2: &str, #[case] expected: usize) {
64 let distance = s1.edit_distance(&s2);
65 assert_eq!(distance, expected);
66 }
67}