CHƯƠNG 2. ỨNG DỤNG SO KHỚP MẪU TRONG QUÁ TRÌNH PHÁT HIỆN XÂM NHẬP MẠNG
2.2 Thuật toán Aho-Corasick
Knuth, Morris và Pratt đưa ra thuật toán KMP [7], [8] dựa trên tiếp cận tiền tố bằng cách không so sánh mẫu với lần lượt từng vị trí trong văn bản như trong thuật toán Brute Force mà có thể dịch chuyển mẫu sang phải văn bản một số vị trí do sử dụng những thông tin của lần thử trước cho lần thử sau. Điều này được thực hiện như Hình 2.5 dưới đây.
Hình 2.5 Quá trình so sánh của thuật toán KMP
Giả sử ta đang kiểm tra đến vị trí thứ j + i trên văn bản và đã có i ký tự đầu của mẫu đã khớp với một đoạn u nào đó của văn bản x 0..i y j j.. i u. Nếu ký tự thứ i + 1 không khớp (tương ứng với vị trí thứ (j + i + 1) trong văn bản. Tức là
1 1
a x i y j i b thì ta không phải so sánh ký tự thứ nhất của mẫu với i - 1 ký tự tiếp theo trong văn bản. Ta dịch cửa sổ đi một số vị trí sao cho thỏa mãn v là phần đầu của xâu x khớp với phần đuôi của xâu u trên văn bản. Hơn nữa ký tự c ở ngay sau v trên mẫu phải khác với ký tự a. Trong những đoạn như v thoả mãn các tính chất trên ta chỉ quan tâm đến đoạn có độ dài lớn nhất.
Thuật toán KMP cải tiến thời gian thực hiện bằng cách giảm số phép so sánh dựa trên các mẫu được tiền xử lý để tìm ra các mẫu con từ đó xây dựng mảng Next để xác định ký tự tiếp theo trong mẫu được kiểm tra dựa trên mô hình otomat. Việc so khớp được thực hiện dựa trên các ký tự trong xâu văn bản T và dịch trạng thái của mảng Next. Ví dụ mẫu P = “aabaaa”, ta sẽ tiền xử lý để xây dựng mảng Next.
Hình 2.6 Xây dựng mảng Next ứng với mẫu P = “aabaaa“
Mảng Next lưu trữ các vị trí quay lui của otomat được xây dựng bởi mẫu P =
“aabaaa”. Vị trí quay lui của ký tự “a” trong otomat được xây dựng bởi mẫu P là vị trí
mẫu P là vị trí thứ 0, 1, 3, 4, 5 có giá trị lần lượt là 0, 0, 0, 0, 3. Mảng Next thu được có giá trị là (0, 0, 2, 0, 0, 3).
Ví dụ: Minh họa hoạt động của thuật toán KMP. Xét mẫu x = “GCAGAGAG”
và văn bản y = ”GCATCGCAGAGAGTATCAGTACG”
Từ mẫu x ta xây dựng được mảng kmpNext lưu trữ các vị trí quay lui của otomat được xây dựng bởi mẫu x là ( - 1, 0, 0, - 1, 1, - 1, 1, - 1, 1)
i 0 1 2 3 4 5 6 7 8 x[i]
kmpNext[i]
G C A G A G A G - 1 0 0 - 1 1 - 1 1 - 1 1 Giai đoạn tìm kiếm:
Lần 1:
Ta bắt đầu so sánh các ký tự của mẫu x với văn bản y. Đến vị trí thứ 4 thấy ký tự “G” trong mẫu x khác với ký tự “T” trong văn bản thì sẽ dịch chuyển mẫu. Tại vị trí này thì i = 3 và giá trị kmpNext[3] = - 1 khi đó sẽ có bước dịch chuyển như sau
Dịch chuyển 4: (i kmpNext i [ ] 3 1) Lần 2:
Tiếp tục so sánh các ký tự của mẫu x với văn bản y bắt đầu tại vị trí mới trong văn bản y. Tại vị trí này thấy ký tự “G” trong mẫu x khác với ký tự “C” trong văn bản thì sẽ dịch chuyển mẫu. Tại vị trí này thì i = 0 và giá trị kmpNext[0] = - 1 khi đó sẽ có bước dịch chuyển như sau
Dịch chuyển 1: ( i kmpNext i [ ] 0 1)
Tương tự như vậy cho các lần dịch chuyển tiếp theo và đối chiếu các vị trí quay lui trong mảng kmpNext[i].
Lần 3:
Dịch chuyển 7 ( i kmpNext i [ ] 8 1)
Lần 4:
Dịch chuyển 1 ( i kmpNext i [ ] 1 0)
Lần 5:
Dịch chuyển 1 ( i kmpNext i [ ] 0 1)
Lần 6:
Dịch chuyển 1 (ikmpNext i[ ] 0 1) Lần 7:
Dịch chuyển 1 ( i kmpNext i [ ] 0 1)
Lần 8:
Dịch chuyển 1 ( i kmpNext i [ ] 0 1)
Trong ví dụ trên, qua 8 lần dịch chuyển thì thuật toán KMP dùng 18 phép so sánh. Tuy nhiên trong thực tế, thuật toán KMP làm việc không tốt đối với tìm kiếm trong văn bản ngôn ngữ tự nhiên, bởi vì nó chỉ có thể bỏ qua các ký tự khi phần đầu của mẫu giống với một phần trong văn bản.
Thuật toán tính mảng Next như sau:
InitNext(p,int m) { int i,j,*kmpNext;
i = - 1; j = 0;
*kmpNext = - 1;
while (j < m) {
while (i > - 1)&&(p[i] ! = p[j]) i = *(kmpNext + i);
i++; j++;
if (p[i] = = p[j])
*(kmpNext + j) = *(kmpNext + i);
else *(kmpNext + j) = i;
}
return kmpNext;
}
Thuật toán Knuth - Morris - Pratt int KMP(char *p, char *t)
{ int i, j, M = strlen(p), N = strlen(t);
InitNext(p);
for (i = 0, j = 0; j < M && i < N; i++, j++) while ((j > = 0) && (t[i] ! = p[j]))
j = next[j];
if (j = = M) return i - M;
else return i;
}
Đánh giá: Thuật toán KMP dùng ít phép so sánh hơn thuật toán BF, độ phức tạp thời gian và không gian để xây dựng bảng kmpNext là O(m)và độ phức tạp tính toán của thuật toán là O(m+n) = O(n).
Thuật toán KMP ở trên chỉ áp dụng trên các mẫu đơn, để mở rộng trên các tập đa mẫu, các tác giả trong [9] đã cải tiến của KMP và gọi là thuật toán Aho-Corasick (AC). Thuật toán AC cho tập đa mẫu sử dụng mô hình otomat hữu hạn (S, Q, I, T, F). Trong đó: Q là tập hữu hạn trạng thái, I là trạng thái bắt đầu, T là tập các trạng thái kết thúc, F là hàm dịch chuyển. Trong đó otomat có thể là đơn định (DFA) hoặc không đơn định (NFA) với sự kết hợp của 3 hàm Goto function, Failure function, Output function.
Ví dụ, để xây dựng mô hình otomat cho tập mẫu P = {her, their, eye, iris, he, is} ta có biểu diễn trong hình 2.7.
Hình 2.7 Xây dựng mô hình otomat cho tập mẫu P = {her, their, eye, iris, he, is}
Khi xây dựng được otomat như hình vẽ trên ta áp dụng thuật toán KMP đối với so khớp đơn mẫu cho từng nhánh của otomat này. Thuật toán AC là cách sử dụng thuật toán KMP cho đơn mẫu nhiều lần.
Thuật toán được mô tả hình thức như sau:
Thuật toán Aho-Corasick 1: Procedure AC (y,n,q0) Input
y ← array of n bytes representing the text input n ← integerrepresenting the text leght
q ← initial state
2: state ← q0
3: For i = 1 → n do → Matching
4: While g(state, y [i]) = fail do → whileg(state, y [i]) is undefined 5: state ← f(state) → use the failure function
6: end while
7: state←g(state, y [i]) 8: Ifo(state) ≠ỉ then
9: output I → This an accepting state, i.e. state ∈A 10: end if
11:end for
12:end procedure
Độ phức tạp thời gian của thuật toán AC là O(n) độc lập với kích thước chuỗi vào và cho tốc độ thực thi tốt với tập nhiều mẫu.