problema cautarea naiva algoritmul Knuth-Morris-Pratt algoritmul Boyer-Moore algoritmul Rabin-Karp cazul mai multor pattern-uri expresii regulate Tipul de date abstract String obiecte: siruri de elemente apartinind unui tip abstract Character operatii inserarea unui subsir eliminarea unui subsir concatenarea a doua siruri regasirea unui sir ca subsir al altui sir sirul in care se cauta se numeste subiect; il notam s[0..n-1] sirul a carui aparitie este cautata in subiect se numeste pattern; il notam p[0..m-1] Cautare naiva: proprietati nu necesita preprocesare; spatiu suplimentar: O(1); totdeauna deplaseaza pattern-ul cu o unitate la dreapta; comparatiile pot fi facute in orice ordine; complexitatea cautarii: O(mn) numarul mediu de comparatii intre caractere: 2n .
s p
Cautarea naiva: algoritm function pmNaiv(s, n, p, m) begin i -1 while (i < n-m) do i i+1 j 0 while (s[i+j] = p[j]) do if (j = m-1) then return i else j j+1 return -1 end Algoritmul KMP: proprietati realizeaza comparatiile de la stanga la dreapta; preprocesare in timpul O(m) cu spatiu suplimentar O(m); complexitatea timp a cautarii: O(n+m) (independent de marimea alfabetului); Algoritmul KMP: ideea a b a b * a b a b c ? Algoritmul KMP: ideea = subiect pattern i j . . . . . . subiect pattern = i j k 0 . . . . . . . . . . . . . . . subiect pattern = j k 0 . . . . . . . . . . . . . . . i Algoritmul KMP: functia esec procedure determinaFctEsec(p,m,f) begin f[0] -1 for j 1 to m-1 do k f[j-1] while (k = -1 and p[j-1] = p[k]) do k f[k] f[j] k+1 end Algoritmul KMP: functia esec: exemplu p = abaabaaabc 5 0 1 2 3 4 6 7 8 a b a a b a a a b c 9
-1 j 0 1 2 3 4 5 6 7 8 9 p[j] a b a a b a a a b c f[j] -1 0 0 1 1 2 3 1 1 2 Algoritmul KMP function KMP(s, n, p, m, f) begin i 0 j 0 while (i < n) do while (j = -1 and s[i] = p[j])do j f[j] if (j = m-1) then return i-m+1 else i i+1 j j+1 return -1 end Algoritmul Boyer-Moore: proprietati comparatiile sunt realizate de la dreapta la stanga; preprocesare in timpul O(km) si spatiu suplimentar O(k), unde k = #Character; complexitatea timp a cautarii: O(mn); 3n comparatii de caractere in cazul cel mai nefavorabil pentru un pattern neperiodic; cea mai buna performanta: O(n / m) .
Algoritmul Boyer-Moore: ideea 0 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 V I S U L U N E I N O P T I D E I A R N A I A R 0 1 2 I A R 0 1 2 I A R 0 1 2 I A R 0 1 2 I A R 0 1 2 I A R 0 1 2 I A R 0 1 2 I A R 0 1 2
= = = 0 1 2 3 4 5 6 7 8 9 10 Algoritmul Boyer-Moore: functia salt cazul cind caracterul apare o singura data in pattern AMAR MAR salt[A] = 1 cazul cind caracterul apare de mai multe ori in pattern SAMAR AMAR salt[A] = 1 Algoritmul Boyer-Moore: salt i+salt[A] i u A A B A nu e in u j salt[A] m-j i+m-j i u A A B A e in u j salt[A] < m-j Algoritmul Boyer-Moore function BM(s, n, p, m, salt) begin i m-1; j m-1 repeat if (s[i] = p[j]) then i i-1 j j-1 else if (m-j) > salt[s[i]] then i i+m-j else i i+salt[s[i]] j m-1 until (j<0 or i>n-1) if (j<0) then return i+1 else return -1 end Algoritmul Rabin-Karp: proprietati utilizeaza o functie hash; preprocesare in timpul O(m) si spatiu suplimentar O(1); cautare in timpul O(mn); timpul mediu: O(n+m) . Algoritmul Rabin-Karp: ideea 0 2 1 ] 1 [ ... ] 1 [ ] 0 [ d m p d p d p y m m + + + =
0 2 1 ] 1 [ ... ] 1 [ ] [ d m i s d i s d i s x m m i + + + + + =
0 1 1 ] [ ) ] [ ( d m i s d d i s x x m i i + + =
+ 0 m-1 p i i+m-1 s i+1 i+m s Algoritmul Rabin-Karp function RK(s, n, p, m) begin dlam1 1 for i 1 to m-1 do dlam1 (d*dlam1)%q hp 0 for i 0 to m-1 do hp (d*hp+index(p[i]))%q hs 0 for i 0 to m-1 do hs (d*hs+index(s[i]))%q i 0 while (i < n-m) do if (hp = hs and equal(p, s, m, i)) then return i hs (hs+d*q-index(s[i])*dlam1)%q hs (d*hs+index(s[i+m]))%q i i+1 return -1 end Algoritmul Rabin-Karp: implementare C #define REHASH(a, b, h) ((((h)-(a)*d) << 1) (b)) int RK(char *p, int m, char *s, int n) { long d, hs, hp, i, j; /* Preprocesare */ for (d = i = 1; i < m; ++i) d = (d << 1); for (hp = hs = i = 0; i < m; ++i) { hp = ((hp << 1) + p[i]); hs = ((hs << 1) + s[i]); } /* Cautare */ i = 0; while (i <= n-m) { if (hp == hs && memcmp(p, s + i, m) == 0) return i; hs = REHASH(s[i], s[i + m], hs); ++i; } return -1; } Mai multe pattern-uri 0 1 2 3 4 5 6 7 8 9 10 A B C D E C D E B C patternul desemneaza o multime de siruri de cautat exemplu: {ABCDE, CDE, BC} Mai multe pattern-uri (continuare) 0 1 2 3 4 5 6 7 8 9 10 A B C D E C D E B C Expresii regulate patternul desemneaza o multime infinita de siruri de cautat = limbajul generat de o expresie regulata definitia expresiilor regulate peste A <expr_reg> ::= a | | empty | ((expr_reg)(expr_reg)) | ((expr_reg) + (expr_reg)) | (expr_reg)* limbajul definit de expresiile regulate L(a) = {a} L() = {} L(empty) = L(e 1 e 2 ) = L(e 1 )L(e 2 ) = {uv | u eL(e 1 ), v e L(e 2 )} L(e 1 +e 2 ) = L(e 1 ) L(e 2 ) L(e*) = i L(e i ) = i L(e) i
Automatul asociat unei expresii regulate a A 1 A 2 a e A e 1 e 2
empty Automatul asociat unei expresii regulate (continuare) A 1 A 2 A 1 (e 1 e 2 ) (e 1 + e 2 ) e 1 * A 1 A 2 Automatul asociat unei expresii regulate: exemplu e = a(b*a+cd) s = abbcacdaaab 1 2 3 4 5 7 6 a b c d a Algoritm de cautare structuri de date D = coada cu restrictii la iesire, unde inserarile se pot face si la inceput si la sfarsit iar stegerile/citirile numai la inceput. q = starea curenta a automatului, j pozitia curenta in textul s, i pozitia in textul s de inceput a "pattern"-ului curent Simbolul # va juca rolul de delimitator (el poate fi inlocuit cu starea invalida -1). Initial avem D = (#), q = 1 (prima stare dupa starea initiala 0), i = j = 1. Algoritm de cautare: pasul curent Daca din q pleaca doua arce neetichetate , atunci insereaza la inceput in D destinatiile celor doua arce; din q pleaca un singur arc etichetat cu s[j] atunci insereaza la sfarsitul lui D destinatia arcului; q este delimitatorul # atunci: daca D = , atunci incrementeaza i, j devine noul i, insereaza # in D si atribuie 1 lui q (aceasta corespunde situatiei cand au fost epuizate toate posibilitatile de a gasi in text o aparitie a unui sir specificat de "pattern" care incepe la pozitia i); daca D , atunci incrementeaza j si insereaza # la sfarsitul lui D; q este starea finala atunci s-a gasit o aparitie a unui sir specificat de "pattern" care incepe la pozitia i. Extrage starea de la inceputul lui D si o memoreaza in q, dupa care reia pasul curent.