background image

A

LGORYTMY

 

I

 S

TRUKTURY

 

D

ANYCH

 

 

dr Edyta Łukasik 

background image

P

ROBLEM

 

Dany jest wzorzec oraz tekst. 

Należy wyszukać wszystkie wystąpienia wzorca w 
podanym tekście. 

10  11  12  13  14  15  16  17 

A  L  I 

A  B  A 

A  R 

A  B  A  B  A 

A  L  I 

Wzorzec: ABA 
Wystąpienia: 

5, 10, 12 

Tekst 

background image

A

LGORYTM

 

NAIWNY

 

procedure Naiwny ( napis, wzor : String); 
var 
  dw, dt, i, j : integer; 
begin 
  dw := length (wzor); 
  dt := length (napis); 
  for i:=1 to dt-dw+1 do 
    begin 
      j:=0; 
      while (wzor[j+1]=napis[i+j]) and (j<dw) do j:=j+1; 
      if  (j=dw) then   writeln(’Wzorzec jest od pozycji’,i); 
    end; 
end; 

background image

A

LGORYTM

 

NAIWNY

 - 

WNIOSKI

 

Złożoność tego algorytmu: 

wykonuje się dt*(dt-dw+1) porównań; 

dw – dł. wzorca, dt – dł. tekstu; 

zwykle dw<<dt więc koszt jest dt*dw. 

Tak  wysoka  jego  złożoność  eliminuje  go  w 

praktyce z przeszukiwania zbiorów binarnych. 

Jedyną  jego  zaletą  jest  prostota,  ale  długi  czas 
działania bierze górę. 

background image

A

LGORYTM

 KMP 

Idea  tego  algorytmu  polega  na  zbadaniu  wzorca, 

w  celu  obliczenia  ilości  pozycji,  o  które  należy 
przesunąć  wskaźnik,  gdy  stwierdzona  zostanie 
niezgodność  wzorca  i  tekstu.  Nie  zawsze  należy  
badać wzorzec od początku. Może się zdarzyć, że 
prefiks  jest  ten  sam  i  tę  informację  trzeba  umieć 
wykorzystać. 

Równolegle  algorytm  ten  odkryli  D.  E.  Knuth  i 
V. R. Pratt oraz J. H. Morris

background image

A

LGORYTM

 KMP – 

TABLICA

 

PRZESUNIĘĆ

(1) 

void tworzShiftKMP ( int dw, string wzor, int s[ ]) 
{ //w stringu elementy są od indeksu 0 

  int   i, k; 
  s[0]=0; s[1]=0; 
  k=0; 
  for(i=2;i<=dw;i++) 
  { 
      while  (k>0 && wzor[k]!=wzor[i-1])  k = s[k]; 
      if   (wzor[k] == wzor[i-1])  k++; 
      s[i] = k; 
  } 

Wynikowa 

tablica 

background image

A

LGORYTM

 KMP – 

IMPLEMENTACJA

(2) 

void   KMP (int dw, int dn, string wzor, string napis) 

  int   i, q; 
  int P[dn+1]; 
  q=0;   i=1; 
  tworzShiftKMP(dw, wzor, P); 
  while (i<=dn-dw+1) 
    { 
      while (q<dw && wzor[q]==napis[q+i-1])  q++; 
      if  (q>=dw) cout<<"Wzorzec jest od pozycji "<<i<<endl; 
      if  (q-P[q]>1) i+=q-P[q]; else i++; 
      q=P[q]; 
    } 

Długość wzorca 

Długość  napisu 

background image

A

LGORYTM

 KMP – 

PRZYKŁAD

 1 

1.

Wzorzec: A B C D A B C 

 

Tablica przesunięć dla wzorca: 

                  [

0

0

0

0

0

1

2

3]

2.

Wzorzec: A B A B A B 

 

Tablica przesunięć dla wzorca:  

                 [

0

0

0

1

2

3

,

 4]

3.

Wzorzec: A B C E A B C D A B C E 

 

Tablica: przesunięć dla wzorca:  

                 [

0

0

0

0

0

1

2

3

,

 0

,

1

,

 2

,

 3

,

 4]

   

background image

A

LGORYTM

 KMP – 

PRZYKŁAD

 2 

Wzorzec: ALARALA   Tablica P = [0,0,0,1,0,1,2,3] 

Tekst: MALARFALARALACA 

i=1    

M

ALARFALARALACA 

q=0   

A

LARALA                         

  i:=i+1=2 

 

i=2                 M

ALAR

FALARALACA 

q=P[0]=0          

ALAR

ALA      

  q=1,...,4  

                                                         i:=i+q-P[q]=2+4-0=6 

background image

A

LGORYTM

 KMP – 

PRZYKŁAD

 2 

C

.

D

10 

i=6                MALAR

F

ALARALACA 

q=P[4]=0                    

A

LARALA              

 i:=i+1=7 

 

i=7               MALARF

ALARALA

CA 

q=P[0]=0                      

ALARALA

            

 q=1,...,7  

                                                      i:=i+q-P[q]=7+7-1=13 

 

i=13:    13 > (15-7+1) więc już nie sprawdzamy! 

background image

A

LGORYTM

 KMP – 

WNIOSKI

 

Algorytm  ten  działa  w  czasie  proporcjonalnym  do 
dn+dw. Jest on rzędu O(dn+dw). 

Największy zysk jest widoczny przy użyciu go dla 
tekstów o wysokim stopniu samopowtarzalności. 

Użycie  jego  jest  wskazane  w  przeglądaniu 
liniowym tekstu bez buforowania, gdyż nie cofamy 
się po przeglądanym tekście. 

Algorytm  ten  można  znacznie  zoptymalizować, 
jeżeli  często  zdarza  się  szukanie  tego  samego 
wzorca.  Wtedy  należy  „wbudować”  tablicę 
przesunięć znanego wzorca. 

11 

background image

A

LGORYTM

 B

OYERA

-M

OORE

A

 

W  algorytmie  tym  porównywany  jest  ostatni  znak 
wzorca.  

Jeżeli  rozpatrywany  znak  nie  występuje  we 
wzorcu,  wówczas  można  przesunąć  się  o  całą 
długość wzorce do przodu. 

Jeżeli występuje, wówczas przesuwamy się tak ze 
wzorcem, aby te same znaki nałożyły się na siebie 
i porównywanie jest kontynuowane. 

Jest to algorytm badania niezgodności. 

12 

background image

13 

void BM(int dw, int dn, string wzor, string napis) 
{  int tabs[k+1], i, j, x,f; 
   tworzShiftBM(dw,wzor,tabs); 
   i=0; 
   while (i<=dn-dw+1) 
     { 
        j=dw; 
        while (napis[i+j]==wzor[j] && j>0)  j--;  
        if  (j<=0)     
             {cout<<"Wzorzec od pozycji "<<i<<endl; i++;} 
        else 
          {    x = tabs[index(napis[i+j])]; 
               if (dw+1-j>x) i += dw+1-j; 
               else   i += x; 
           } 
    } 

 

A

LGORYTM

 B

OYERA

-M

OORE

A

 

background image

A

LGORYTM

 BM – 

TABLICA

 

PRZESUNIĘĆ

 

14 

Informuje ona o ile liter należy się przesunąć, licząc od końca 

wzorca, aby trafić na właściwy znak. 

void tworzShiftBM (int dw, string w, int ts[]) 

     int i; 
     for(i=0;i<=k;i++) ts[i]=dw+1; 
     for(i=0;i<=dw;i++) ts[index(w[i])]=dw-i; 

  
 Funkcja indeks zwraca dla spacji 0 w pozostałych 
 przypadkach numer litery w alfabecie. 

background image

A

LGORYTM

 BM - 

PRZYKŁAD

 

10 

11  12 

Znak  A  B  C  D  E  F  G  H  I 

K  L  Ł  M  N  O  P  R 

Index  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44 

Tabs 

15 

M=4

 ,   

x=4

  

             

i:=i+1 

background image

A

LGORYTM

 BM - 

WNIOSKI

 

Algorytm  ten  jest  najbardziej  praktyczny,  gdy 

alfabet jest dostatecznie duży i wzorzec względnie 
długi. 

Koszt  działania  tego  algorytmu  wynosi  O(dn), 

gdzie dn jest długością przeglądanego tekstu. 

Porównywanie symboli od strony prawej do lewej 
jest  bardzo  efektywnym  sposobem.  W  najlepszym 

przypadku  algorytm  przegląda jedynie  dn/dt  część 
całego tekstu. 

16 

background image

A

LGORYTM

 K

ARPA

-R

ABINA

 

Wzorzec  do  wyszukania  jest  traktowany  jako 
klucz  o  długości  M  znaków,  któremu  przypisana 
jest  pewna  wartość  funkcji  H(w).  Obliczana  jest 
ona dla wzorca tylko raz. 

Tekst  jest  analizowany  po  M  kolejnych  znaków. 
Obliczana jest dla nich wartość funkcji H(t). 

Te  dwie  wartości  H(w)  i  H(t)  są  ze  sobą 
porównywane.  Jeżeli  są  one  równe,  wtedy 
sprawdzamy  znak  po  znaku  fragment  tekstu  i 
wzorzec. 

17 

background image

A

LGORYTM

 KR – 

OBLICZANIE

 

KLUCZA

 

Każdy znak będziemy interpretować jako pewną 
cyfrę  dziesiętną.  W  przypadku  ogólnym  jako 
cyfrę w systemie o podstawie d. 

Wtedy  H(w)  możemy  obliczyć  z  reguły Hornera 
w czasie O(M). 

Obliczenie H(t

s+1

) jest bardzo proste, jeżeli mamy 

już  obliczone  H(t

s

):  usuwamy  z  t

s

  cyfrę 

najbardziej  znaczącą,  przemnażamy  wszystko 
przez  podstawę,  aby  przesunąć  cyfry  o  jedną 
pozycję  w  lewo  i  dodajemy  cyfrę  najmniej 
znaczącą. 

18 

background image

A

LGORYTM

 K

ARPA

-R

ABINA

 (1) 

19 

   Najpierw  należy  obliczyć  hasz  dla  wzorca  i  hasz 

dla  pierwszych  tylu  znaków  tekstu,  ile  wynosi 

długość wzorca: 
 
   b= 10;    
   p= 33554393;   //p – duża liczba pierwsza 
   hashW=0;   
   hashR=0; 
   for(i=1;i<=dlwzorca;i++) 
    { 
        hashW=(hashW*b+index(wzor[i])) % p; 
        hashR=(hashR*b+index(napis[i])) % p; 
     } 

background image

A

LGORYTM

 K

ARPA

-R

ABINA

 (2) 

20 

   bM1=1;  
   for(i=1;i<=dlwzorca-1;i++)  
 

bM1 = (b*bM1) %  p; //b do potęgi m-1 

   for(i=1;i<=dltekstu-dlwzorca+1;i++) 
   { 
      if (hashW==hashR)  
        { 
             j=0; 
             while (wzor[j+1]==napis[i+j] && j<dlwzorca)  j++; 
             if (j==dlwzorca) cout<<"wzorzec od pozycji "<<i<<endl; 
        }        
          hashR=(hashR+b*p-index(napis[i])*bM1) % p;        
          hashR=(hashR*b+index(napis[i+m])) % p;             
    }  

background image

A

LGORYTM

 KR – 

WNIOSKI

 

Jako  podstawę  systemu  (nasze  b)  należy  wybrać 
wielokrotność 

liczby 

2. 

Można 

wtedy 

zaimplementować operację mnożenia przez b jako 
przesunięcie bitowe (np. dla b=64 o <<6 ). 

Dla  dużego  p  (liczba  pierwsza)  zmniejszamy 
prawdopodobieństwo 

wystąpienia 

kolizji 

spowodowanej niejednoznacznością funkcji H. 

Koszt algorytmu O(M+N). 

21 

background image

A

LGORYTM

 KR – 

PRZYKŁAD

 

22 

Alfabet  składa  się  z  liter:  A,  B,  C,  D.  Za  podstawę  systemu 
przyjmiemy 

więc  liczbę  4.  Wartości  liter  wynoszą 

odpowiednio: A-0, B-1, C-2, D-3. Za moduł przyjmiemy liczbę 
pierwszą 23. 

Szukany wzorzec to: BDA

Tekst ma postać: CDBDACCAA

Obliczamy hash wzorca: 

H(W) = (1

4

2

+3 

4

1

+0 

4

0

) mod 23 = 28 mod 23 = 5 

 

Obliczmy hash pierwszych trzech znaków tekstu: 

H(T) = (2

4

2

+3 

4

1

+1 

4

0

) mod 23 = 45 mod 23 = 22 

background image

A

LGORYTM

 KR – 

PRZYKŁAD

 

23 

 

Jeżeli  H(W)

H(T)  (5

22),  wtedy  wzorzec  na  tej  pozycji 

na pewno nie wystąpił. 

 

Sprawdzamy  kolejną  sekwencję  znaków  o  długości  3 

przesuwając się względem poprzedniej sekwencji o jeden znak: 

                

C

DB 

 DB

D

  

H(T) = ( H(T) - (4

mod 23) 

2) mod 23 = (22-16 

2) mod 23 = 

= -10 mod 23 = 13 – usunięcie najwyższej cyfry (

C

H(T) = H(T)

4 mod 23 = 13

4 mod 23 = 52 mod 23 = 6  

– przesunięcie o rząd wielkości 

H(T) = ( H(T) + 3 ) mod 23 = (6+3) mod 23 = 9  

– dodanie ostatniej cyfry (

D

background image

A

LGORYTM

 KR – 

PRZYKŁAD

 

24 

H(T) = 9 

 5 = H(W)  

Więc to nie może być wzorzec, szukamy dalej!!! 

Sprawdzamy  kolejną  sekwencję  znaków  o  długości  3 
przesuwając się względem poprzedniej sekwencji o jeden znak: 

 

D

BD 

 BD

A

  

H(T) = ( H(T) - (4

mod 23)

3) mod 23 = (9-16

3) mod 23 = 

= -39 mod 23 = 7 – usunięcie najwyższej cyfry (

D

H(T) = H(T)

4 mod 23 = 7

4 mod 23 = 5  

– przesunięcie o rząd wielkości 

H(T) = ( H(T) + 0 ) mod 23 = (5+0) mod 23 = 

 – dodanie ostatniej cyfry (

A

background image

A

LGORYTM

 KR – 

PRZYKŁAD

 

25 

H(T) = 5 = H(W) 

 

Mamy  więc  sekwencję  podejrzaną  o  identyczną  z 

wzorcem. Teraz algorytmem naiwnym znak po znaku należy 
przejrzeć tę sekwencję i wzorzec co do zgodności znaków. 

Wzorzec: BDA 

Sekwencja (fragment tekstu od pozycji 3): BDA. 

 

Pierwsze wystąpienie wzorca zostało więc znalezione 

w tekście. Występuje on w szukanym tekście i zaczyna się od 
pozycji 3.