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

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

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

Thông tin tài liệu

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

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 5P 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 13Ta thấy c<>b nên j=5.Tương ứng kí tự P[5] và T[5]. VD2: 0 1 2 3 4 5P 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 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 7Dễ 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 12Dễ 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: Để hiểu hơn,bạn hãy thử với P= abcab và T=acbabbdababcabcabbGiả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.  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-1for (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. 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 / k4. 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 bca bcd” -> Prefix(4)=1 P=”ab cab cd” -> Prefix(5)=2 P=”abc abc d” -> 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=”ab cab c” -> 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=”abc abc ” Vậy PI(6)=PI(5)+1=3. VD2: P=”abcababcabc” q=11 P=”abcab abcab c” -> 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=”abc ababcabc ” -> PI(11)=3 VD3: P=”abcabcabcaa” q=11 P=” abcabca bca a” -> 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] 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=”abcab abcab d” -> 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=02.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]=0P=”abcabca” -> PI[2]=0P=”abcabca” -> PI[3]=0P=”a bca bca” -> PI[4]=1P=”ab cab ca” -> PI[5]=2P=”abc abc a” -> PI[6]=3P=”abca bca ” -> PI[7]=4q=0 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=”abcabcabca a 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=1i=12: T=”abcabcabca ab abcba” P[1+1]=T[12] -> q=1+1=2 P=”abcabca”i=13: T=”abcabcabcaab a bcba” P[2+1]<>T[13] -> q=PI[2]=0 P=”abcabca” P[0+1]=T[13] -> q=0+1=1 i=14: T=”abcabcabcaab ab cabca” P[1+1]=T[14] -> q=1+1=2 P=”abcabca”i=15: T=”abcabcabcaab abc ba” P[2+1]=T[15] -> q=2+1=3 P=”abcabca”i=16: T=”abcabcabcaababcba” P[3+1]<>T[16] -> q=PI[3]=0 P=”abcabca” P[0+1]<>T[16] -> q=0i=17: T=”abcabcabcaababcb a ” P[0+1]=T[17] -> q=0+1=1 P=”abcabca  Độ phức tạp:O(m+n) . 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. 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

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

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan