Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Thuật toán Levenshtein Distance (Khoảng cách chỉnh sửa) trong C++

Levenshtein Distance (Edit Distance) đo lường độ khác biệt giữa 2 chuỗi: số phép toán tối thiểu (chèn, xóa, thay thế) để biến chuỗi này thành chuỗi kia.

Ví dụ

  • "cat" → "mat": 1 (thay 'c' → 'm')
  • "kitten" → "sitting": 3 (k→s, e→i, chèn g)
  • "tutorials" → "point": 8

Quy hoạch động (Dynamic Programming)

Bảng dp[i][j] = khoảng cách giữa s1[0..i-1] và s2[0..j-1].

  • Cơ sở: dp[0][j] = j (chèn j ký tự), dp[i][0] = i (xóa i ký tự).
  • Chuyển trạng thái:
        if (s1[i-1] == s2[j-1]) cost = 0; else cost = 1;
        dp[i][j] = min(
            dp[i-1][j] + 1,        // Xóa
            dp[i][j-1] + 1,        // Chèn
            dp[i-1][j-1] + cost    // Thay thế / giữ
        );
        

Chương trình C++ tối ưu không gian

#include <bits/stdc++.h>
using namespace std;

// O(n*m) time, O(min(n,m)) space
int levenshtein(const string& s1, const string& s2) {
    int m = s1.size(), n = s2.size();
    if (m < n) return levenshtein(s2, s1); // Đảm bảo s1 dài hơn
    
    vector prev(n + 1), curr(n + 1);
    iota(prev.begin(), prev.end(), 0); // 0, 1, 2, ..., n
    
    for (int i = 1; i <= m; ++i) {
        curr[0] = i;
        for (int j = 1; j <= n; ++j) {
            int cost = (s1[i-1] == s2[j-1]) ? 0 : 1;
            curr[j] = min({prev[j] + 1,        // Xóa
                           curr[j-1] + 1,      // Chèn
                           prev[j-1] + cost}); // Thay thế
        }
        swap(prev, curr);
    }
    return prev[n];
}

// Phiên bản trả về cả thao tác (traceback)
vector getOperations(const string& s1, const string& s2) {
    int m = s1.size(), n = s2.size();
    vector> dp(m+1, vector(n+1));
    for (int i = 0; i <= m; ++i) dp[i][0] = i;
    for (int j = 0; j <= n; ++j) dp[0][j] = j;
    
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j) {
            int cost = (s1[i-1] == s2[j-1]) ? 0 : 1;
            dp[i][j] = min({dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost});
        }
    
    // Truy vết
    vector ops;
    int i = m, j = n;
    while (i > 0 || j > 0) {
        if (i > 0 && j > 0 && dp[i][j] == dp[i-1][j-1] + (s1[i-1]==s2[j-1]?0:1)) {
            if (s1[i-1] != s2[j-1]) ops.push_back("Thay '" + string(1,s1[i-1]) + "' -> '" + string(1,s2[j-1]) + "'");
            --i; --j;
        } else if (i > 0 && dp[i][j] == dp[i-1][j] + 1) {
            ops.push_back("Xoa '" + string(1,s1[i-1]) + "'");
            --i;
        } else {
            ops.push_back("Chen '" + string(1,s2[j-1]) + "'");
            --j;
        }
    }
    reverse(ops.begin(), ops.end());
    return ops;
}

int main() {
    string a = "tutorials", b = "point";
    cout << "Levenshtein(\"" << a << "\", \"" << b << "\") = " 
         << levenshtein(a, b) << '\n';
    
    cout << "\nCac buoc bien doi:\n";
    for (const auto& op : getOperations(a, b))
        cout << " - " << op << '\n';
    
    // Test thêm
    cout << "\nkitten -> sitting: " << levenshtein("kitten", "sitting") << '\n'; // 3
    cout << "saturday -> sunday: " << levenshtein("saturday", "sunday") << '\n'; // 3
    return 0;
}

Kết quả

Levenshtein("tutorials", "point") = 8

Cac buoc bien doi:
 - Xoa 't'
 - Xoa 'u'
 - Xoa 't'
 - Xoa 'o'
 - Xoa 'r'
 - Xoa 'i'
 - Xoa 'a'
 - Xoa 'l'
 - Xoa 's'
 - Chen 'p'
 - Chen 'o'
 - Chen 'i'
 - Chen 'n'
 - Chen 't'
(Chú ý: traceback đơn giản có thể không tối ưu về số bước in ra, nhưng khoảng cách = 8 là đúng)

kitten -> sitting: 3
saturday -> sunday: 3

Ứng dụng thực tế

  • Spell checker / Autocorrect (tìm từ gần nhất trong từ điển).
  • DNA sequence alignment (bioinformatics).
  • Plagiarism detection, diff tools (git diff).
  • Speech recognition (đo sai số nhận dạng).
  • Fuzzy string matching (tìm kiếm gần đúng).

Biến thể

Biến thểChi phí phép toánỨng dụng
Levenshtein chuẩnInsert=1, Delete=1, Replace=1Chung
Damerau-Levenshtein+ Transpose (hoán vị 2 ký tự liền kề)=1Typo thường gặp (teh→the)
Weighted Edit DistanceTrọng số khác nhau cho từng phépOCR, DNA (transition/transversion)
Longest Common Subsequence (LCS)Chỉ cho phép Insert/DeleteDiff, version control

Levenshtein Distance là thuật toán kinh điển, nền tảng của nhiều ứng dụng xử lý ngôn ngữ tự nhiên và so sánh chuỗi.