3.3.1 Giới thiệu thuật toán Boyer Moore
Thuật toán Boyer Moore [10] là thuật toán có tìm kiếm chuỗi rất có hiệu quả trong thực tiễn, các dạng khác nhau của thuật toán này thường được cài đặt trong các chương trình soạn thảo văn bản.
Khác với thuật toán Knuth-Morris-Pratt (KMP), thuật toán Boyer-Moore kiểm tra các ký tự của mẫu từ phải sang trái và khi phát hiện sự khác nhau đầu tiên thuật toán sẽ tiến hành dịch cửa sổ đi Trong thuật toán này có hai cách dịch của sổ:
Cách thứ 1: gần giống như cách dịch trong thuật toán KMP, dịch sao cho những phần đã so sánh trong lần trước khớp với những phần giống nó trong lần sau. Trong lần thử tại vị trí j, khi so sánh đến ký tự i trên mẫu thì phát hiện ra sự khác nhau, lúc đó x[i+1…m]=y[i+j...j+m-1]=u và a=x[i]y[i+j-1]=b khi đó thuật toán sẽ dịch cửa sổ sao cho đoạn u=y[i+j…j+m-1] giống với một đoạn mới trên mẫu (trong các phép
dịch ta chọn phép dịch nhỏ nhất)
Nếu không có một đoạn nguyên vẹn của u xuất hiện lại trong x, ta sẽ chọn sao cho phần đôi dài nhất của u xuất hiện trở lại ở đầu mẫu.
Cách thứ 2: Coi ký tự đầu tiên không khớp trên văn bản là b=y[i+j-1] ta sẽ dịch sao cho có một ký tự giống b trên xâu mẫu khớp vào vị trí đó (nếu có nhiều vị trí xuất hiện b
trên xâu mẫu ta chọn vị trí phải nhất)
Nếu không có ký tự b nào xuất hiện trên mẫu ta sẽ dịch cửa sổ sao cho ký tự trái nhất của cửa sổ vào vị trí ngay sau ký tự y[i+j-1]=b để đảm bảo sự ăn khớp Trong hai cách dịch thuật toán sẽ chọn cách dịch có lợi nhất.
Thuật toán Boyer-Moore có thể đạt tới chi phí O(n/m) là nhờ có cách dịch thứ 2 “ký tự không khớp”. Cách chuyển cửa sổ khi gặp “ký tự không khớp” cài đặt vừa đơn giản lại rất hiệu quả trong các bảng chữ cái lớn nên có nhiều thuật toán khác cũng đã lợi dụng các quét mẫu từ phải sang trái để sử dụng cách dịch này. Tuy nhiên chi phí thuật toán của Boyer-Moore là O(m*n) vì cách dịch thứ nhất của
thuật toán này không phân tích triệt để các thông tin của những lần thử trước, những đoạn đã so sánh rồi vẫn có thể bị so sánh lại. Có một vài thuật toán đã cải tiến cách dịch này để đưa đến chi phí tính toán của thuật toán Boyer-Moore là tuyến tính.
So khớp chuỗi (String Matchching) Ý nghĩa của ký tự đại diện (wildcard)
Có hai ký tự wildcard thông thường là ? và * . Dấu hỏi đại diện cho một ký tự duy nhất bất kỳ.
Dấu sao (*) đại diện cho nhiều ký tự bất kỳ hoặc không có ký tự nào cả. Bạn hãy quan sát vài ví dụ dưới đây:
Mẫu Chuỗi so sánh Kết quả w?ldcard wildcard khớp w?ldcard waldcard khớp
w*ldcard wldcard khớp (không có ký tự) w*ldcard willdcard khớp (có hai ký tự) w*ldcard* wldcards khớp
Việc xử lý dấu hỏi khá đơn giản. Nếu không có dấu * trong chuỗi mẫu, ta chỉ cần duyệt lần lượt qua từng cặp ký tự trong chuỗi mẫu và chuỗi cần so sánh. Nếu gặp ký tự ? bên chuỗi mẫu thì ta luôn luôn bỏ qua (coi như cặp ký tự ở hai chuỗi là khớp). Ta chỉ cần lưu ý là hai chuỗi phải có cùng chiều dài.
Hàm kiểm tra và cài đặt thuật toán Boyer-Moore
unsigned long CURIFilter::bmSearch(unsigned char *TargetMem,unsigned char *SourceMem,unsigned long TargetMemLen,unsigned long SourceMemLen,bool& searchresult)
{//cai dat thuat toan booyer moore
unsigned char searchstr[256]; // Max pattern length is 256.
unsigned char alfa[256]; // Remember where alphabetchars are located. unsigned char skipsInsens[256]; // Skips array.
unsigned char patlenInsens; // Length of search pattern. int LenIndex = 0;
// Get length and make 'smalls'copy. unsigned char c = SourceMem[LenIndex]; while ( c != '\0') { alfa[LenIndex] = 0; if (c >= 'A'&& c <= 'Z') { searchstr[LenIndex] = c + 0x20; alfa[LenIndex] = 1; } else { searchstr[LenIndex] = c; if (c >= 'a'&& c <= 'z') alfa[LenIndex] = 1; } c = SourceMem[++LenIndex]; }
int len = LenIndex + 1; // For chars not in pattern. for (int i = 0; i < 255; i++)
skipsInsens[i] = len; //length + 1.
// For chars in pattern.
// with double chars only right most survives. for ( int i = 0; i < LenIndex; i++)
{
skipsInsens[searchstr[i]] = LenIndex - i;
}
patlenInsens = LenIndex;
// Check if searchstr is longer than TargetMem or zero length. if (patlenInsens > TargetMemLen || patlenInsens == 0)
{
searchresult = false; return -1;
}
unsigned long Position = 0;
unsigned char * end = TargetMem + TargetMemLen; int PatternLenIndex = patlenInsens -1;
int j;
for(;;) {
// main loop for comparing strings. j = PatternLenIndex;
while (
((TargetMem[j] == searchstr[j]) ||
((alfa[j]) && (TargetMem[j] == (char) (searchstr[j]-0x20)))) && --j >= 0);
if (j != -1) {
// Mismatch; align and check for end of TargetMem. unsigned char* skipindex = (unsigned char*) TargetMem + patlenInsens;
if (skipindex >= end) {
searchresult = false;
return -1; // string not in TargetMem. } TargetMem += skipsInsens[*skipindex]; Position +=skipsInsens[*skipindex]; } else { // Match found. searchresult = true; return Position; } } return -1; }
int CURIFilter::CheckPattern(unsigned char* in_szPattern, unsigned long in_ulPatternLen, int* out_matched)//kiem tra chuoi co thoa dieu kien hay khong ? {
bool bResult = false;
for (int i = 0; i < (int)m_pFilterObj->m_vecPatternSearch.size(); i++) { unsigned char *key=m_pFilterObj->m_vecPatternSearch[i].key;
bmSearch(in_szPattern,key,in_ulPatternLen,(unsigned long)_mbslen(key),bResult);// so khop mau
if (bResult == true) { bResult = false; bmSearch(in_szPattern,m_pFilterObj- >m_vecPatternSearch[i].val,in_ulPatternLen,(unsigned long)_mbslen(m_pFilterObj- >m_vecPatternSearch[i].val),bResult); if (bResult==true) { *out_matched = i; return 1; } } /*if(_mbsstr(s,r.key)!=NULL&& _mbsstr(s,r.val)!=NULL) { *out_matched = i; return 1; } */ } return 0; }