1. Trang chủ
  2. » Công Nghệ Thông Tin

Bài toán tìm kiếm xâu mẫu

7 941 18
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Bài toán tìm kiếm xâu mẫu
Trường học Trường Đại Học Bách Khoa Hà Nội
Thể loại bài báo
Thành phố Hà Nội
Định dạng
Số trang 7
Dung lượng 83,89 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Bài toán tìm kiếm xâu mẫu

Trang 1

Bài toán tìm kiếm xâu mẫu

Đề bài: Cho xâu T độ dài n (gọi là văn bản_text) Cho P độ dài m (gọi là xâu mẫu_pattern) Tìm tất cả các vị trí khớp của P trong T

Giải: Có 4 thuật toán sau:

1 Tìm kiếm trực tiếp:

 Ý tưởng:Dịch từng vị trí s=0,1, n-m, với mỗi vị trí xem xâu mẫu có xuất hiện ở

vị trí đó không

 Code:

void NaiveSM(char* P,int m,char* T,int n){

int i,j;

for(j=0;j<=n-m;j++){

for (i=0;i<m&&P[i]==T[i+j];i++)

if (i>=m) OUTPUT(j);

}

}

 Độ phức tạp: O(nm)

2 Thuật toán Boyer-Moore:

 Ý tưởng:

 Hàm int Last(char c,char* P): Trả vị trí cuối cùng của c trong xâu mẫu P Nếu c không xuất hiện trong P thì giá trị trả lại là -1

VD: P= ”abcabdacgj”

Vị trí 0123456789

=> Last(‘a’,P)=6 Last(‘d’,P)=5 Last(‘p’,P)=-1

 Dựa trên cơ sở hàm Last, ta sẽ xây dựng các bước nhảy để tăng tính tốc độ duyệt.Thuật toán như sau:

+ Gọi s là vị trí cần khảo sát Ban đầu s=0

P a c a b a c

T a a b a c b d c a c a b a c

s=0

+ Lặp chừng nào s<=n-m:

So sánh 2 xâu P và T, lần lượt từ vị trí cuối cùng, cho tới khi gặp các kí tự khác nhau.Gọi đó là kí tự thứ j trong xâu P, tương ứng vị trí s+j trong T: VD1:

0 1 2 3 4 5

P a c a b a c

T a a b a c b d c a c a b a c

s=0 1 2 3 4 5 6 7 8 9 10 11 12 13

Ta thấy c<>b nên j=5.Tương ứng kí tự P[5] và T[5]

VD2:

0 1 2 3 4 5

P a c a b a c

T a a b a c b d c a c a b a c

s=4 5 6 7 8 9

Trang 2

j=3 Kí tự P[3] vàT[7].

 Nếu j=-1 => Đây là vị trí khớp, xuất s

Sau đó dịch phải bình thường(s++)

 Trái lại, gọi c=T[s+j].Xét Last(c,P):

• TH1: Last(c,P)<j Ta dịch P để vị trí Last[c,P] trùng với vị trí s+j của xâu T:

VD:

0 1 2 3 4 j=5

P: a c a b a c -> j=5 , c=’b’

T: a a b a c b d c a a c last(c,P)=3 s=0 1 2 3 4 5

Sau khi dịch:

0 1 2 3 4 5 P: a c a b a c

T: a a b a c b d c a a c s=2 3 4 5 6 7

Dễ thấy thao tác dịch là s=s+j- last(c,P)

Ở VD trên ta dịch được 2 vị trí->tốt hơn dịch tuần tự

• TH2: Last(c,P)=-1 Kí tự c không xuất hiện trong P.Dịch toàn bộ P ra sau vị trí s+j của T:

VD:

0 1 2 3 4 j=5

P: a c a b a c -> j=5 , c=’d’ T: a a b a c b d c a a c e f last(c,P)=-1 s=1 2 3 4 5 6

Sau khi dịch:

0 1 2 3 4 5 P: a c a b a c

T: a a b a c b d c a a c e f s=7 8 9 10 11 12

Dễ thấy thao tác dịch vẫn là s=s+j- last(c,P)

• TH3: Nếu Last(c,P)>j Ta chỉ dịch phải 1 vị trí (s++) VD:

0 1 2 j=3 4 5

P: a c a b a c -> j=3 , c=’c’

T: a a b c a c d c a a c last(c,P)=5 s=0 1 2 3 4 5

Sau khi dịch:

0 1 2 3 4 5 P: a c a b a c

T: a a b c a c d c a a c s=1 2 3 4 5 6

 VD tổng hợp:

Trang 3

Để hiểu hơn,bạn hãy thử với P= abcab và T=acbabbdababcabcabb

Giải:

0 1 j=2 3 4 P: a b c a b

T: a c b a b b d a b a b c a b c a b b s=0 1 2 3 4

0 1 2 j=3 4 P: a b c a b

T: a c b a b b d a b a b c a b c a b b s=1 2 3 4 5

0 1 2 3 j=4 P: a b c a b T: a c b a b b d a b a b c a b c a b b s=2 3 4 5 6

0 1 2 3 j=4 P: a b c a b T: a c b a b b d a b a b c a b c a b b s=7 8 9 10 11

j=-1 0 1 2 3 4 P: a b c a b T: a c b a b b d a b a b c a b c a b b s=9 10 11 12 13

->xuất s=9.

0 1 2 3 j=4 P: a b c a b

T: a c b a b b d a b a b c a b c a b b s=10 11 12 13 14

j=-1 0 1 2 3 4 P: a b c a b T: a c b a b b d a b a b c a b c a b b s=12 13 14 15 16

->xuất s=12.

0 1 2 3 4 P: a b c a b

T: a c b a b b d a b a b c a b c a b b s=13 14 15 16 17 Như vậy có 2 vị trí khớp : s=9 và s=12

Trang 4

 Code:

s=0;

while (s<=n-m) {

j= m-1;

while ((j>=0)&&(T[j+s]==P[j])) j ;

if (j==0) {

OUTPUT(s);

s++;

}

else {

k=last(T[j+s],P);

s=s+ max( j-k,1);

}

}

 Độ phức tạp:

Hàm Last: O(m+<kích th ước bảng chữ cái>)

Chương trình:Tình huống tồi nhất O(mn+ +<kích thước bảng chữ>)

VD: P=bam-1 T=an

Kém hiệu quả với bảng chữ nhỏ

3 Thuật toán Rabin Karp:

 Ý tưởng: Chuyển đổi P và các xâu con độ dài m của T sang số nguyên (n-m+1 số) Bài toán quy về tìm 1 số trong dãy n-m+1 số đã cho

Gọi kích thước bảng chữ là k

P sẽ chuyển thành:

p= km-1P[0] + km-2P[1] + +P[m-1]

=( (P[0] * k + P[1])*k + P[2]) )*k + P[m-1] (Sơ đồ Horne)

Độ phức tạp O(m)

Với các xâu con của T, nếu tính trực tiếp như trên phải có độ phức tạp:

(n-m+1) * O(m)=O((n-m+1)m)

-> Tốn kém

Tuy nhiên, ta có thể tính số sau theo số trước:

VD: Số trước: a1a2a3a4 ->t1

Số sau : a2a3a4a5 ->t2

-> t2= (t1 % km-1)*k + a5

Cách tính này chỉ có độ phức tạp O(n):

t[0]=0; offset=1;

for (i=0;i<m-1) offset* = k; //offset = km-1 for (i=0;i<m;i++) t[0]=2*t[0]+T[i];

for(s=1;s<=n-m;s++) t[s]=(t[s-1] % offset) *k + T[s+m-1];

Tóm lại thuật toán có độ phức tạp O(m+n)

 Nhược điểm:

Các số p,t có thể rất lớn ,vượt quá các kiểu dữ liệu cơ bản->Các phép toán không còn là O(1) nữa

Trang 5

Khắc phục: tính toán theo modul (p,t tính theo số dư khi chia cho 1 số q nào đó).Tuy nhiên như vậy dẫn đến 1 số xâu khác nhau vẫn có thể cho các số giống nhau.Vì vậy khi tìm được số t=p, ta phải kiểm tra xem vị trí đó có thật sự là khớp hay không

Nên chọn q đủ lớn,<= Max_nguyên / k

4 Thuật toán Knuth-Morris-Pratt:

 Ý tưởng:

Để dễ mô tả,ta coi các xâu đánh số từ 1

 Xâu W gọi là tiền tố(prefix) của xâu X nếu X có dạng WY (Y là 1 xâu nào đó) VD: X=”qetyughjk” W=”qety”

Xâu W gọi là hậu tố(suffix) của xâu X nếu X có dạng YW (Y là 1 xâu nào đó) VD: X=”qetyughjk” W=”yughjk”

Nếu có thêm W<> X thì W gọi là prefix(hay suffic) thực sự của X

 Hàm int Prefix(int q):

Hàm trả độ dài của prefix dài nhất của P[1 m] đồng thời là suffix thực sự của P[1 q]

VD: P=”abcabcd”

P=”abcabcd” -> Prefix(1)=0

P=”abcabcd” -> Prefix(2)=0

P=”abcabcd” -> Prefix(3)=0

P=”a bcabcd” -> Prefix(4)=1

P=”abcabcd” -> Prefix(5)=2

P=”abcabcd” -> Prefix(6)=3

P=”abcabcd” -> Prefix(7)=0

Ta xây dựng PI(k)=Prefix(k) với k=1->m:

+ Dễ thấy PI[1]=0

+ Giả sử đã có các PI(k) với mọi k<q

Ta sẽ tính PI(q)

VD1: P=”abcabc” q=6

P=”abcabc” -> PI(5)=2

Khi bổ sung kí tự P[3], ta thấy nó khớp với “ab” thành “abc” là suffic của xâu P[1 6]: P=”abcabc”

Vậy PI(6)=PI(5)+1=3

VD2: P=”abcababcabc” q=11

P=”abcababcabc” -> PI(10)=5

Khi bổ sung kí tự P[6], ta thấy nó ghép với “abcab” thành “abcaba” không phải là suffic của xâu P[1 11]

Nhưng xâu Prefix của “abcab” (tức “ab”) thì khớp với kí tự tiếp theo(P[3]

=”c”) tạo thành xâu “abc” chính là suffic của P[1 11]

P=”abcababcabc” -> PI(11)=3

VD3: P=”abcabcabcaa” q=11

P=” abcabcabcaa” -> PI(10)=7

Khi bổ sung kí tự P[8] ,ta thấy nó ghép với “abcabca” thành “abcabcab” không phải là suffic của xâu P[1 11]

Xét xâu Prefix của “abcabca” (tức “abca”).Nó ghép với kí tự tiếp theo(P[5]

=”b”) tạo thành xâu “abcab” vẫn không là suffic của P [1 11]

Trang 6

Xét xâu tiếp Prefix của “abca” (tức “a”).Nó ghép với kí tự tiếp theo(P[2]

=”b”) tạo thành xâu “ab” vẫn không là suffic của P [1 11]

Xét xâu tiếp Prefix của “a” là “”.Nó ghép với kí tự tiếp theo(P[1] =”a”) tạo thành xâu “a” là suffic của P [1 11]

V ậy: PI(11)=1 (Bạn có thể tự kiểm tra)

VD4: P=”abcababcabd” q=11

P=”abcababcabd” -> PI(10)=5

Khi bổ sung kí tự P[6] ,ta thấy nó ghép với “abcab” thành “abcaba” không phải là suffic của xâu P[1 11]

Xét xâu Prefix của “abcab” (tức “ab”).Nó ghép với kí tự tiếp theo(P[3 ]

=”c”) tạo thành xâu “abc” vẫn không là suffic của P [1 11]

Xét xâu Prefix của “abc”(tức “”).Nó ghép với kí tự tiếp theo(P[1]=”a”) tạo thành xâu “a” vẫn không là suffic của P [1 11]

V ậy PI(11)=0

Từ đó ta có thuật toán tính Prefix:

1 PI [1]= 0 ; k=0;

2 for (q=2;q<=m;q++)

while ((k>0) && (P[k+1]<>P[q]))

k=PI[k]

if (P[k+1]== P[q]) k++

PI[q]=k;

 Thuật toán: Dựa trên hàm Prefix nêu trên ta có thuật toán tìm kiếm xâu mẫu:

Ý tưởng là xác định độ dài q của xâu vừa là prefix của P,vừa là suffix của T[1 i] với i = 1->n Ta thấy rằng nếu q=m thì vị trí khớp chính là i-m+1

Cách tính q gần như cách tính Prefix

1.q=0

2.for i=1 to n

while q>0 and P[q+1]<>T[i]

q=PI[q]

if P[q+1]==T[i] then q++

if q==m then

OUTPUT(i-m+1)

q=PI[q]

VD: T=”abcabcabcaababcba” P=”abcabca”

P=”abcabca” -> PI[1]=0

P=”abcabca” -> PI[2]=0

P=”abcabca” -> PI[3]=0

P=”a bcabca” -> PI[4]=1

P=”abcabca” -> PI[5]=2

P=”abcabca” -> PI[6]=3

P=”abcabca” -> PI[7]=4

q=0

Trang 7

i=1: T=”abcabcabcaababcba” P[0+1]=T[1] -> q=0+1=1

P=”abcabca”

i=2: T=”abcabcabcaababcba” P[1+1]=T[2] -> q=1+1=2

P=”abcabca”

i=3: T=”abcabcabcaababcba” P[2+1]=T[3] -> q=2+1=3

P=”abcabca”

i=4: T=”abcabcabcaababcba” P[3+1]=T[4] -> q=3+1=4

P=”abcabca”

i=5: T=”abcabcabcaababcba” P[4+1]=T[5] -> q=4+1=5

P=”abcabca”

i=6: T=”abcabcabcaababcba” P[5+1]=T[6] -> q=5+1=6

P=”abcabca”

i=7: T=”abcabcabcaababcba” P[6+1]=T[7] -> q=6+1=7 -> Xuất s=1

P=”abcabca” q=PI[7]= 4

->P=”abcabca”

i=8: T=”abc abcab caababcba” P[4+1]=T[8] -> q=4+1=5

P=”abcabca”

i=9: T=”abc abcabc aababcba” P[5+1]=T[9] -> q=5+1=6

P=”abcabca”

i=10: T=”abc abcabca ababcba” P[6+1]=T[10] -> q=6+1=7 -> Xuất s=4

P=”abcabca”

->P=”abcabca”

i=11: T=”abcabcabcaa babcba” P[4+1]<>T[11] -> q=PI[4]=1

P=”abcabca” P[1+1]<>T[11] -> q=PI[1]=0

P[0+1]=T[11] -> q=0+1=1

i=12: T=”abcabcabcaab abcba” P[1+1]=T[12] -> q=1+1=2

P=”abcabca”

i=13: T=”abcabcabcaab bcba” P[2+1]<>T[13] -> q=PI[2]=0a

P=”abcabca” P[0+1]=T[13] -> q=0+1=1

i=14: T=”abcabcabcaab cabca” P[1+1]=T[14] -> q=1+1=2ab

P=”abcabca”

i=15: T=”abcabcabcaab ba” P[2+1]=T[15] -> q=2+1=3abc

P=”abcabca”

i=16: T=”abcabcabcaababcba” P[3+1]<>T[16] -> q=PI[3]=0

P=”abcabca” P[0+1]<>T[16] -> q=0

i=17: T=”abcabcabcaababcb ” P[0+1]=T[17] -> q=0+1=1a

P=”abcabca

 Độ phức tạp:O(m+n)

Ngày đăng: 05/11/2012, 11:58

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w