Hướng dẫn thuật toán

Trùm CUỐI 2026-01-11 10:56:35

Link bài tập

1. Phân tích bài toán

  • Yêu cầu: Tìm một xâu con chung có độ dài lớn nhất từ hai xâu s t .
  • Đặc điểm: Xâu con không cần các ký tự phải đứng liên tiếp nhau nhưng phải giữ đúng thứ tự xuất hiện.
  • Kỹ thuật: Sử dụng bảng Quy hoạch động (DP) để lưu trữ độ dài LCS của các tiền tố, sau đó truy vết để tìm xâu kết quả.

2. Ý tưởng thuật toán

Bước 1: Xây dựng bảng DP

  • Gọi dp[i][j] là độ dài của xâu con chung dài nhất của xâu con s[0...i-1] t[0...j-1] .
  • Công thức truy hồi:
    • Nếu ký tự tại vị trí tương ứng giống nhau ( s[i-1] == t[j-1] ): dp[i][j] = dp[i-1][j-1] + 1 .
    • Nếu khác nhau ( s[i-1] \neq t[j-1] ): dp[i][j] = \max(dp[i-1][j], dp[i][j-1]) .
  • Cơ sở: dp[0][j] = 0 dp[i][0] = 0 (khi một trong hai xâu rỗng).

Bước 2: Truy vết để tìm xâu kết quả

Sau khi điền đầy bảng dp , giá trị tại dp[|s|][|t|] chính là độ dài LCS. Để lấy được xâu cụ thể, ta bắt đầu từ ô cuối cùng (|s|, |t|) và đi ngược về ô (0, 0) :

  • Nếu s[i-1] == t[j-1] : Ký tự này thuộc LCS. Ta thêm nó vào kết quả và di chuyển chéo lên ô dp[i-1][j-1] .
  • Nếu s[i-1] \neq t[j-1] : Di chuyển sang ô có giá trị lớn hơn giữa dp[i-1][j] dp[i][j-1] .
  • Lặp lại cho đến khi i=0 hoặc j=0 .
  • Lưu ý: Xâu thu được từ việc truy vết sẽ bị ngược, cần đảo ngược lại để có kết quả cuối cùng.

3. Lưu ý khi triển khai

  • Kích thước mảng: Với giới hạn N, M \le 3000 , bảng dp sẽ có khoảng 3001 \times 3001 phần tử (kiểu int). Bộ nhớ tiêu tốn khoảng 36MB, hoàn toàn nằm trong giới hạn cho phép.
  • C++: Nên khai báo mảng dp ở phạm vi toàn cục (global) hoặc dùng std::vector<vector<int>> để tránh tràn bộ nhớ stack.
  • Python: Do tốc độ xử lý vòng lặp của Python chậm hơn, hãy sử dụng danh sách (list) thay vì các thư viện nặng nề nếu không cần thiết, và chú ý giới hạn đệ quy nếu dùng cách tiếp cận đệ quy có nhớ.
  • Truy vết: Khi truy vết, ưu tiên di chuyển theo hướng có giá trị bằng dp[i][j] để đảm bảo tính chính xác.