MỤC LỤC
MỤC LỤC
1
I – GIỚI THIỆU
2
II- THUẬT TOÁN BOYER MOORE
2
III- SỐ PHÉP GÁN VÀ SO SÁNH 7
IV – ĐỘ PHỨC TẠP CỦA THUẬT TOÁN 10
V- KẾT LUẬN:
13
VI – TÀI LIỆU THAM KHẢO
13
1
I – GIỚI THIỆU
Thuật toán Boyer-Moore được đưa ra năm 1977 để kiểm tra, tìm kiếm
xâu cho một văn bản T[1,2,…,n] có chiều dài n với mỗi T[i] là một kí tự
trong bảng chữ cái (alphabet) với kích thước là 256 ký tự khác nhau. và một
xâu mẫu P[1,2,…,m] có chiều dài m (m≤n). Tìm (các) vị trí xuất hiện của
xâu P (như là một xâu liên con liên tục) trong văn bản T.
Như vậy, thuật toán Boyer-Moore sẽ đối sánh T và P theo chiều từ phải
sang trái của P. Bằng cách áp dụng thêm luật kí tự tồi (bad character rule) và
luật hậu tố tốt.
II- THUẬT TOÁN BOYER MOORE
1. Luật kí tự tồi:
Xuất hiện sự khác nhau giữa mẫu P và văn bản T: b = T [i+ j]# P[ i]=,
ta sẽ dịch sao cho có một ký tự giống b trên mẫu khớp vào vị trí đó, nếu có
nhiều vị trí xuất hiện b trên mẫu ta chọn vị trí bên phải nhất. Nếu không có ký
tự b nào trong mẫu ta sẽ dịch sao cho ký tự trái nhất của mẫu vào vị trí ngay
sau ký tự T [i+ j]= b để đảm bảo sự ăn khớp. Hai hướng tiếp cận sẽ tạo ra 2
giá trị dịch chuyển khác nhau, từ đó sẽ lựa chọn giá trị lớn hơn làm giá trị
dịch chuyển.
Trước hết chúng ta hãy xem xét ví dụ sau để hiểu luật này.
T=FINDIFAHAYXEACKNEXDLET=FINDIFAHAYXEACKNEXDLE
P=NEXDLEP
Tại bước đầu tiên (i=1)so sánh T[1,2,…,6] với P[1,2,…,6] ta thấy
rằng T[6]≠P[6] (chú ý ta so sánh từ phải sang trái của P). Do T[6]∉P, ta ngay
lập tức có thể dịch P lên 6 đơn vị và bắt đầu so sánh T[7,8,…,12] với P[1,2,
…,6]
Tại bước này ta sẽ gặp vị trí không khớp T[11]≠P[5], vì T[11]=P[3], ta sẽ
dịch P lên 2 đơn vị để cho T[11] thẳng hàng với P[3] và tiếp tục so sánh T[9,
…,14] với P[1,2,…,6]
2
Tại bước này, T[14]≠P[6] và T[14]∉P, ta dịch P lên 6 đơn vị và so sánh
T[15,…,20] với P[1,2,…,6]].
Tiếp tục các bước như trong hình vẽ dưới đây, cuối cùng ta tìm được
mẫu P ở vị trí i=16.
1
2
F I
3
4
5
N D I
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
F A H A Y X E A C K N E X D L E
N E X D L E
N E X D L E
N E X D L E
N E X D L E
N E X D L E
Hình 1 – Quá trình so khớp mẫu P=”NEXDLE” của thuật toán Boyer-Moore
Luật kí tự tồi: Dịch P lên một số x đơn vị sao cho kí tự tồi T[k] khớp
với kí tự P[s] là kí tự xuất hiện gần với P[m] nhất (s lớn nhất). Nếu s>js>j
hoặc không tồn tại kí tự P[s] như vậy thì ta dịch P lên 1.
Để thực thi luật kí tự tồi, với mỗi kí tự ∈Σ, gọi R[σ] là vị trí xuất hiện của kí
tự σgần với P[m] nhất. Nếu σ∉P, R[σ]=0. Theo quy luật kí tự tồi, mỗi bước
chúng ta sẽ dịch P một đoạn là: max(1,j−R[T[k]])
3
Index
x 2
x 1
x 4
x 4
x 4
x 4
Hình 2 – Quá trình dịch P lên một số x đơn vị
Thủ tục thuật giả mã của thuật toán BM sử dụng luật kí tự tồi như
sau:
int BMmatcher(int n, int m){
badCharBackup(m);
int i = 1, j = 0, p = 0, skip = 0;
while(i <= n-m+1){
skip = 0;
j = m;
while(j >= 1 && skip == 0){
if(T[i+j-1] != P[j]){
p = T[i+j-1]-96;
skip = max(1, j - R[p]);
}
j--;
}
if(skip == 0) {
4
return i;
}
i = i + skip;
}
return -1;
}
void badCharBackup(int m){
int i = 0;
memset(R, 0, sizeof(R));
for(i = 1; i <= m; i++){
R[P[i] - 96] = i;
}
}
2. Luật hậu tố tốt (Good suffix rule)
Luật hậu tố tốt chúng ta phát biểu trong phần này mạnh hơn phát biểu
gốc cuả thuật toán Boyer-Moore.
Luật hậu tố tốt: Giả sử khi so sánh, một xâu con Tsub của văn
bản T khớp với hậu tố của mẫu P, nhưng kí tự liền kề bên trái của Tsub không
khớp với kí tự tương ứng trong P. Tìm một xâu con tsub (nếu có) phải nhất
(right most-- gần với kí tự cuối P[m] nhất) của P thỏa mãn: tsub không phải
là hậu tố của P và kí tự liền kề bên trái của tsub khác với kí tự liền kề bên trái
của hậu tố Tsub của P. Dịch P sao cho tsub khớp với Tsub trong T.
Nếu tsub không tồn taị, dịch P một số lượng ít nhất sao cho kí tự đầu
tiên của P vượt quá kí tự đầu tiên của Tsub và tiền tố của P khớp với hậu tố
của Tsub. Nếu không thể dịch P để thỏa mãn điều kiện đó, dịch P lên m đơn vị
Trường hợp ta tìm tất cả sự xuất hiện của P trong T, sau khi đã tìm thấy P, ta
dịch P một lượng ít nhất (> 0) sao cho tiền tố của P khớp với hậu tố
của P trong T. Trong trường hợp không thể dịch như vậy, dịch P lên m đơn vị.
5
Xét ví dụ sau:
T[1,2,…,30]= C G T G C C T A C T T A C T T A C T T A C T T A C G C G A
A
và P[1,2,…,9]= C T T A C T T A C khi so sánh T với P, nhận
thấy Tsub={TAC}=T[7,8,9], và cũng tìm được Tsub=(TAC)=P[3,4,5], ta có
thể dịch 4 đơn vị để khớp với Tsub..
Hình 3: so sánh P với T theo luật Good suffix rule
Thủ tục thuật giả mã của thuật toán BM sử dụng luật kí tự tồi như sau:
int fastfastBMmatcher(int n, int m){
computeLL(m, LL, Zbar);
computeSL(m, SL, Zbar);
while(i <= n-m+1){
skip = 0;
j = m;
while(j >= 1 && skip == 0){
if(T[i+j-1] != P[j]){
p = T[i+j-1]-96;
skip = max(1, j - R[p]);
if(j!=m){
if(LL[j+1] !=0){
6
skip = max(skip, m - LL[j+1]);
} else {
skip = max(skip, m - SL[j+1]);
}
}
}
j--;
}
if(skip == 0) return i;
i = i + skip;
}
return -1;
}
void computeLL(int m, int *LL, int *Zbar){
int i = 0, j = 0;
char Prev[m+1];
int Zrev[m+1];
for(i = m; i >= 1; i--){
Prev[m-i+1] = P[i];
}
compute_Z_function(Prev, Zrev, m);
for(i = 1; i<= m-1; i++){
Zbar[i] = Zrev[m-i+1];
}
for(j = 1; j <= m-1; j++){
i = m - Zbar[j]+1;
LL[i] = j;
}
}
void computeSL(int m, int *SL, int *Zbar){
7
int i = 1,j = 0;
for(i = 1; i
<= m-1; i++){
if(Zbar[i] == i){
SL[m-i+1] = i;
j = i;
} else {
SL[m-i+1] = j;
}
}}
III- SỐ PHÉP GÁN VÀ SO SÁNH
1. Số phép gán
50
100
150
200
250
300
350
400
450
500
550
600
650
700
750
800
850
900
950
1000
113.46
100.78
102.42
89.21
46.86
70.17
29.74
49.45
45.15
19.42
22.00
21.35
14.62
20.25
15.96
13.54
17.67
15.17
14.39
16.71
102.25
101.11
83.28
72.92
56.74
45.83
48.35
38.34
35.15
35.95
29.21
23.71
19.93
19.13
14.85
16.57
14.15
14.59
14.72
13.40
88.68
89.66
81.29
88.50
61.94
43.98
52.58
37.58
37.32
26.88
23.29
15.67
16.53
16.39
13.81
16.66
14.41
12.76
16.14
14.83
82.27
83.91
87.64
88.49
58.62
55.88
55.27
29.29
34.45
34.35
29.83
21.23
20.20
19.04
14.64
15.07
15.74
14.40
13.91
14.65
89.72
103.99
81.33
78.86
57.62
49.93
37.43
37.24
34.95
28.60
26.24
23.26
19.26
17.31
16.13
15.41
12.79
13.88
13.69
12.92
87.62
89.13
77.24
81.59
57.69
45.63
46.14
34.17
33.78
32.25
23.60
20.44
17.68
19.66
14.42
15.04
13.71
13.18
14.51
14.32
173.85
175.76
159.14
173.32
120.45
84.42
101.64
71.66
160.96
164.35
171.69
173.48
113.66
108.14
106.97
55.12
175.73
204.45
159.13
154.34
111.64
96.43
71.30
70.94
171.63
174.77
151.02
159.60
111.96
87.70
88.81
64.81
102.93
101.59
79.77
70.83
59.04
49.21
49.57
38.65
34.66
36.98
23.97
21.26
17.28
19.47
15.83
16.55
14.11
15.99
14.50
13.23
81.98
93.30
91.12
82.85
52.55
54.75
44.64
33.43
31.41
30.35
23.91
20.53
16.74
19.06
17.53
15.56
15.30
15.78
13.66
13.41
92.28
85.16
77.11
83.40
57.71
45.51
42.27
40.20
31.28
30.04
24.24
21.30
17.88
15.90
15.67
15.80
15.55
14.21
14.29
13.76
98.89
93.20
73.93
79.98
59.91
50.93
46.96
34.79
35.47
31.44
23.81
21.12
17.18
18.43
15.68
16.05
15.80
14.64
14.76
13.78
b. Số phép so sánh
50
100
150
200
250
300
350
400
223.10
198.21
201.31
174.89
90.25
136.75
56.20
95.52
201.00
198.77
162.97
142.38
109.93
88.14
93.09
73.00
202.14
199.60
156.06
138.17
114.57
94.96
95.63
73.75
160.49
183.10
178.63
162.17
101.61
105.94
85.84
63.37
180.96
166.82
150.75
163.27
111.93
87.52
81.06
76.86
194.17
182.89
144.45
156.41
116.27
98.32
90.41
66.06
8
450
500
550
600
650
700
750
800
850
900
950
1000
86.69
35.52
40.42
39.35
26.04
36.94
28.48
23.70
31.72
26.87
25.37
29.98
66.67
68.44
54.88
43.95
36.34
34.66
26.26
29.55
24.98
25.72
26.09
23.39
71.05
50.14
43.11
27.94
29.54
29.38
24.17
29.71
25.37
22.02
28.70
26.14
65.44
65.22
56.04
38.94
36.91
34.50
25.89
26.64
27.98
25.32
24.30
25.79
66.49
53.71
49.01
42.98
34.96
31.18
28.71
27.45
22.24
24.29
23.93
22.37
64.01
60.96
43.70
37.39
31.81
35.73
25.42
26.60
24.00
22.90
25.41
25.18
65.89
70.36
44.47
39.08
31.02
35.40
28.15
29.56
24.81
28.40
25.59
23.01
59.39
57.21
44.29
37.58
29.98
34.57
31.50
27.61
27.10
28.01
23.87
23.35
59.08
56.51
45.00
39.16
32.17
28.35
27.84
28.10
27.60
24.90
25.13
24.11
67.37
59.36
44.09
38.69
30.86
33.31
27.79
28.57
27.97
25.81
26.03
24.12
V- KỲ VỌNG, PHƯƠNG SAI, ĐỘ LỆCH CHUẨN, ĐỒ THỊ
5.1 Số phép gán
n
Kỳ vọng
Phương sai
Độ lệch chuẩn
Chặn trên
Chặn dưới
50
100
93.805
105.30705
10.26192233
104.06692
83.54308
93.518
53.33526222
7.303099494
100.8211
86.2149
150
78.37
62.7362
7.920618663
86.290619
70.44938
200
74.838
38.39226222
6.196148983
81.034149
68.64185
250
54.209
14.39016556
3.793437169
58.002437
50.41556
300
48.789
60.87896556
7.802497392
56.591497
40.9865
350
40.685
49.50216111
7.035777222
47.720777
33.64922
400
33.315
25.74407222
5.073861668
38.388862
28.24114
450
29.889
7.19681
2.682687086
32.571687
27.20631
500
24.514
18.51593778
4.303014964
28.817015
20.21099
550
19.498
7.965373333
2.822299299
22.320299
16.6757
600
16.447
3.30289
1.817385485
18.264385
14.62961
650
15.233
1.799867778
1.341591509
16.574592
13.89141
700
14.119
3.123298889
1.767285741
15.886286
12.35171
750
13.45
0.309622222
0.556437078
14.006437
12.89356
800
13.238
0.675173333
0.821689317
14.059689
12.41631
850
12.906
2.103804444
1.450449739
14.35645
11.45555
900
12.728
0.709995556
0.84261234
13.570612
11.88539
950
12.662
0.346595556
0.588723667
13.250724
12.07328
1000
12.623
0.454378889
0.674076323
13.297076
11.94892
Đồ thị biểu diễn
9
5.2 Số phép so sánh
n
Kỳ vọng
Phương sai
Độ lệch chuẩn
Chặn trên
Chặn dưới
50
100
185.514
420.6064267
20.5086915
206.022691
165.00531
185.034
213.2170933
14.6019551
199.635955
170.43204
150
154.738
250.8875511
15.8394303
170.57743
138.89857
200
147.672
153.6067067
12.3938173
160.065817
135.27818
250
106.421
57.52832111
7.58474265
114.005743
98.836257
300
95.578
243.4937511
15.6042863
111.182286
79.973714
350
79.364
198.1224044
14.0755961
93.4395961
65.288404
400
64.628
102.9767067
10.1477439
74.7757439
54.480256
450
57.772
28.71646222
5.35877432
63.1307743
52.413226
500
47.024
74.04389333
8.60487614
55.6288761
38.419124
550
36.991
31.93743222
5.65132128
42.6423213
31.339679
600
30.894
13.20982667
3.63453252
34.5285325
27.259467
650
28.463
7.203512222
2.68393596
31.146936
25.779064
700
26.237
12.49275667
3.5345094
29.7715094
22.702491
750
24.897
1.233356667
1.11056592
26.0075659
23.786434
800
24.48
2.706666667
1.64519502
26.125195
22.834805
850
23.813
8.394756667
2.89737065
26.7103706
20.915629
900
23.45
2.838622222
1.68482112
25.1348211
21.765179
950
23.325
1.387383333
1.17787238
24.5028724
22.147128
1000
23.244
1.81496
1.34720451
24.5912045
21.896795
Đồ thị biểu diễn
10
IV – ĐỘ PHỨC TẠP CỦA THUẬT TOÁN
Xét tổng quát chương trình sau:
int BMmatcher(int n, int m){
int R[28];
int Zbar[MAXM];
int LL[MAXM];
int SL[MAXM];
badCharBackup(m);
int i = 1, j = 0, p = 0, skip = 0;
computeLL(m, LL, Zbar);
computeSL(m, SL, Zbar);
while(i <= n-m+1){
skip = 0;
j = m;
while(j >= 1 && skip == 0){
if(T[i+j-1] != P[j]){
p = T[i+j-1]-96;
skip = max(1, j - R[p]);
if(j!=m){
if(LL[j+1] !=0){
11
skip = max(skip, m - LL[j+1
} else {
skip = max(skip, m - SL[j+1
}
}
}
j--;
}
if(skip == 0) return i;
i = i + skip;
}
return -1;
}
1. Tính toán độ phức tập của chương trình con
Thực hiện tính toán trên số phép so sánh
void badCharBackup(int m) {
int i = 0;
for (i = 0; i < m; i++) {
m+1 lần
R[P[i] - 26] = i;
}
Vậy độ phức tạp O(m+1)
2. Tính toán độ phức tạp của chương trình con
Thực hiện tính toán trên số phép so sánh
void computeLL(int m, int *LL, int *Zbar){
for(i = 1; i<= m-1; i++){
m lần
Zbar[i] = Zrev[m-i+1];
}
for(j = 1; j <= m-1; j++){
m lần
i = m - Zbar[j]+1;
12
LL[i] = j;
}
}
Thực hiện tính toán trên số phép so sánh
void computeSL(int m, int *SL, int *Zbar){
int i = 1,j = 0;
for(i = 1; i <= m-1; i++){
m lần
if(Zbar[i] == i){
m lần
SL[m-i+1] = i;
j = i;
} else {
SL[m-i+1] = j;
}
}
}
Vậy ta tính được độ phức tạp của chương trình trên là O(4m)
3. Tính độ phức tạp trong chương trình chính
Thực hiện tính toán trên số phép so sánh
BOYERMOORE(int
while(i <= n-m+1){
n,
int
m){
n-m+2
skip = 0;
j = m;
while(j >= 1 && skip == 0){
if(T[i+j-1] != P[j]){
m(n-m+2)+1
m(n-m+2)
p = T[i+j-1]-96;
skip = max(1, j - R[p]);
if(j!=m){
if(LL[j+1] !=0){
m(n-m+2)
m(n-m+2)
13
skip = max(skip, m - LL[j+1
} else {
skip = max(skip, m - SL[j+1
}
}
}
j--;
}
if(skip == 0) return i;
m(n-m+2)
i = i + skip;
}
}
Vậy ta tính được độ phức tạp là O(n-m+1 +m(n-m+1)+1+4(m(n-m)))
=O(5(m(n-m)+n+1)
Tổng kết lại ta có: O (m+1) +O(4m)+ O(5(m(n-m)+n+1)
Kết luận: vậy độ phức tạp của thuật toán là O(m*n)
V- KẾT LUẬN:
Thuật toán Boyer Moore là thuật toán 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.
Các đặc điểm chính của nó: Thực hiện việc so sánh từ phải sang trái. Giai
đoạn tiền xử lý (preprocessing) có độ phức tạp thời gian và không gian là O(m
+C). Giai đoạn tìm kiếm có độ phức tạp lớn nhất O(m*n).
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.
14
Tuy nhiên 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 như: BMH (BMHorspool) [1] đưa ra năm 1980 dùng cho tập đơn mẫu. CW (Commentz &
Walter) [10] đưa ra năm 1979, SBMH [11] năm 2002 và YBHK [12] năm
2003, WM (Wu & Manner) năm 1994 dùng cho tập đa mẫu. Với CW thì độ
phức tạp thời gian trong trường hợp tốt nhất với tập p mẫu là: O(n*max{m[0],
m[1],... m[p-1]}) còn trong trong trường hợp tốt nhất thời gian thực thi là
O(n/min{m[0], m[1],... m[p-1]}) với m[i] là độ dài của mẫu thứ i
VI – TÀI LIỆU THAM KHẢO
[1] Boyer, Robert S., and J. Strother Moore. A fast string searching
algorithm. Communications of the ACM 20.10 (1977): 762-772.
[2]
[3]
15