Chủ Nhật, 12 tháng 8, 2012

Xâu con chung dài nhất


Phương pháp quy hoạch động giải bài toán xâu con chung dài nhất:

1. Bài toán:

Xâu ký tự X được gọi là xâu con của xâu ký tự Y nếu ta có thể xoá đi một số ký tự trong xâu Y để được xâu X.
Cho biết hai xâu ký tự A và B, hãy tìm xâu ký tự C có độ dài lớn nhất và là con của cả A và B.
Input
Dòng 1: chứa xâu A
Dòng 2: chứa xâu B
Output
Chỉ gồm một dòng ghi độ dài xâu C tìm được
Example
Input:
abc1def2ghi3
abcdefghi123

Output:
10

2. Tiếp cận bài toán:

Định nghĩa xâu con của một xâu: một xâu X có dộ dài l là xâu con của xâu Y có độ dài m khi có một dãy c sao cho 1<=c[1]<=c[2]<=c[3]<…<=c[i]<=…c[l]<=m, và X[c[i]]=Y[c[i]], mọi 1<=i<=l. Nói một cách khác, từ xâu gốc ta xóa đi các kí tự tại các vị trí tùy ý, các kí tự còn lại được dồn lại kề nhau và vẫn bảo toàn thứ tự.

Ví dụ: Có xâu X: abcdfr. Các xâu con có thể là a, ab,  bc, bd, dfr, abcdfr,…Các xâu không phải xâu con là ba,cda,…

Để giải quyết bài toán này đầu tiên, ta xét bài toán đơn giản hơn.
Bài toán 1: Có 2 xâu, một xâu có m kí tự, xâu còn lại không có kí tự nào. Với bài toán này dễ thấy độ dài xâu con chung dài nhất là 0 (không có xâu nào).

Bài toán 2: Có hai xâu, mỗi xâu có đúng một kí tự.

Ví dụ 2.1: xâu X: a, xâu Y: b. Dễ thấy độ dài xâu con chung dài nhất là 0, vì không có xâu nào thỏa mãn.
Ví dụ 2.2: xâu X: a, xâu Y: a. Dễ thấy độ dài xâu con chung dài nhất là 1, xâu con chung khi đó là xâu có một kí tự a.

Bài toán 3: Có hai xâu, xâu X là: abc1def2ghi3, xâu Y là: abcdefghi123. Làm thế nào để có đáp án.

Ta giả sữ đã biết cấu trả lời trong trường hợp xâu X có độ dài i-1 kí tự đầu tiên, xâu Y có độ dài j-1 kí tự đầu tiên, khi đó đáp án trong trường hợp xâu X có độ dài i kí tự đầu tiên, xâu Y có độ dài j kí tự
đầu tiên thì sao? F[i-1][j-1],f[i-1][j],f[i][j-1] đã biết, F[i][j]=?.

Xâu X: x1x2…xi-1xi
Xâu Y: y1y2…yj-1yj

Ta có các trương hợp sau:

TH1: xi=yj, như vậy thì f[i][j]=f[i-1][j-1]+1.
Th2: xi!=yj, f[i][j]=max(f[i-1][j],f[i][j-1]).

Các bạn vẽ bảng sau:

1
2
3
4
j-1
j
7
8
9
10
11
12
1












2












3












4












5












i-1




F[i-1][j-1]
F[i-1][j]






i




F[i][j-1]
F[i][j]






8












9












10












11












12













Tính như thế nào? Trong bài này ta tính từng dòng, nghĩa là ta tính hết f[i-1][j], thì sau đó ta mới f[i][j], mọi j.
Trên một dòng muốn tính f[i][j] thì trước hết ta phải tính f[i][j-1].

3.Code:

/*Code được cài trên IDE DevC++*/
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxS 1001


using namespace std;

char a[maxS],b[maxS];
int f[maxS][maxS];


int main(){
    // Doc du lieu
    scanf("%s",&a);
    scanf("%s",&b);
    // Khoi tao mang f
    memset(f,0,sizeof(f));
    int da=strlen(a);
    int db=strlen(b);
    int i,j;
    for(i=1;i<=da;++i)
    for(j=1;j<=db;++j)
        if(a[i-1]==b[j-1]) f[i][j]=f[i-1][j-1]+1;
        else f[i][j]=max(f[i-1][j],f[i][j-1]);
    // Ket qua o f[da][db]
    printf("%d",f[da][db]);   
    return 0;   
}

4. Độ phức tap:

O(m*n) về xử lý.
O(m*n) không gian lưu trữ.

5. Ghi chú:

Code trên đã test trên vn.spoj.pl. Các bạn chú ý tài liệu trên mình biên soạn chỉ dành cho CLB thuật toán khoa CNTT đại học Nha Trang.

Tham khảo:
http://en.wikipedia.org/wiki/Longest_common_subsequence_problem

Không có nhận xét nào:

Đăng nhận xét