1. Algorytm KMP (Knuth, Morris Pratt). - prz

Transkrypt

1. Algorytm KMP (Knuth, Morris Pratt). - prz
Instrukcja IEF Algorytmy i struktury danych
Laboratorium 2: ”Przeszukiwanie tekstów” cd.
1. Algorytm KMP (Knuth, Morris Pratt).
Algorytm KMP opublikowany został w roku 1976 i stanowi rozwinięcie algorytmu naiwnego. Algorytm
naiwny wykonuje wiele powtarzających się przeszukiwań, nie wykorzystując w przypadku niezgodności
któregoś znaku wzorca informacji wcześniej przebadanej. W algorytmie KMP wykorzystywana jest
informacja o możliwości wykonania przesunięcia wzorca o więcej niż jeden znak.
i – indeks tekstu
j – indeks wzorca
N – długość tekstu
M - długość wzorca.
• tworzenie tablicy przesunięć
Tablica przesunięć (zwyczajowo nazwana next) wykorzystywana jest w algorytmie KMP do
wyznaczania maksymalnych bezpiecznych przesunięć wzorca względem przeszukiwanego tekstu.
Tworzona jest według następującego algorytmu:
a) next[0]=-1
j=1
b) umieść kopię pierwszych j znaków wzorca pod fragmentem wzorca złożonym z pierwszych j liter
wzorca, przesuniętych w prawo o jeden znak
c) przesuwaj te kopię w prawo do chwili gdy:
- wszystkie nakładające znaki są identyczne, lub
- nie ma nakładających się znaków
d) next[j] jest równe liczbie nakładających się znaków
j=j+1
jeśli j jest równe długości wzorca – stop,
w przeciwnym wypadku skocz do punktu b).
•
a)
b)
c)
d)
e)
next[0]=-1;
for(i=0,j=-1;i<M-1;i++,j++,next[i]=j)
while((j>=0)&&(wzor[i]!=wzor[j]))
j=next[j];
algorytm KMP - wykorzystanie tablicy przesunięć
i=0 j=0
wyznaczyć tablicę przesunięć next dla danego wzorca
dopóki text[i]=wzor[j] i nie przeszukano całego tekstu zwiększ i oraz j o jeden
jeśli przeszukano cały tekst, wtedy możliwe są dwa przypadki:
jeśli j=M to wzorzec zaczyna się na pozycji i-M,
w przeciwnym przypadku wzorzec nie został znaleziony
ponieważ tekst[i]!=wzor[j], więc należy skoczyć do c) zmieniając j na next[j]. Jeśli j=-1, należy
zwiększyć i oraz j o jeden i również skoczyć do c).
for(i=0,j=0;i<N && j<M; i++, j++)
while((j>=0) && (tekst[i]!=wzor[j]))
j=next[j];
if(j==M)
return i-M;
else
return -1;
Przykład
Znaleźć wzorzec BABAABBB w tekście BABABAABBABAABBB
indeks T
i
tekst T
wzorzec W
j
indeks W
next
0
↓
B
B
↑
0
-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
A
A
B
B
A
A
B
A
A
B
A
B
B
B
B
A
B
A
A
B
B
B
1
0
2
0
3
1
4
2
5
0
6
1
7
1
Ponieważ znaki text[i] i wzor[j] są zgodne, następuje przesunięcie i oraz j aż do momentu stwierdzenia
niezgodności lub znalezienia całego wzorca.
W przykładzie niezgodność występuje na pozycji 4:
indeks T
i
tekst T
wzorzec W
j
indeks W
next
0
1
2
3
B
B
A
A
B
B
A
A
0
-1
1
0
2
0
3
1
4
↓
B
A
↑
4
2
5
6
7
8
9
10
11
12
13
14
15
A
B
A
B
B
B
B
A
B
A
A
B
B
B
5
0
6
1
7
1
Z tego powodu konieczne jest przesunięcie wzorca tak, aby znak o indeksie next[4] (czyli 2), oznaczony
pismem pogrubionym znalazł się pod znakiem o indeksie i (oznaczonym kolorem szarym):
indeks T
i
tekst T
wzorzec W
j
indeks W
next
0
1
2
3
B
A
B
B
A
A
0
-1
1
0
4
↓
B
B
↑
2
0
5
6
7
8
9
10
11
12
13
14
15
A
A
A
A
B
B
B
B
A
B
B
A
A
B
B
B
3
1
4
2
5
0
6
1
7
1
Ponieważ znaki tekstu o indeksach od 4 do 8 są zgodne z odpowiednimi znakami (od 2 do 6) wzorca, więc
następuje przesunięcie indeksów i oraz j zgodnie z c). Różne znaki występują na pozycji 9:
indeks T
i
tekst T
wzorzec W
j
indeks W
next
0
1
2
3
4
5
6
7
8
B
A
B
B
A
A
B
B
A
A
A
A
B
B
B
B
0
-1
1
0
2
0
3
1
4
2
5
0
6
1
9
↓
A
B
↑
7
1
10
11
12
13
14
15
B
A
A
B
B
B
Z tego powodu konieczne jest przesunięcie wzorca (poprzez zmianę j) tak, aby znak o indeksie next[7]
(czyli 1), oznaczony pismem pogrubionym znalazł się pod znakiem o indeksie i (oznaczonym kolorem
szarym):
indeks T
i
tekst T
wzorzec W
j
indeks W
next
0
1
2
3
4
5
6
7
8
B
A
B
A
B
A
A
B
B
B
0
-1
9
↓
A
A
↑
1
0
10
11
12
13
14
15
B
B
A
A
A
A
B
B
B
B
B
B
2
0
3
1
4
2
5
0
6
1
7
1
Ponieważ wszystkie pozostałe znaki są zgodne z wzorcem, wzorzec został odnaleziony na pozycji 16-8=8.
Wartość 8 wynika z wartości indeksu i o jeden większej od długości napisu.
2. Algorytm BM (Boyera i Moore’a)
W algorytmie tym w przeciwieństwie do algorytmu KMP porównywany jest ostatni znak wzorca. Jeśli
badany znak ( o indeksie i) tekstu nie wchodzi w skład wzorca, możliwe jest przesunięcie indeksu i o
długość wzorca. Jeśli znak ten występuje – konieczne jest przesunięcie wzorca tak, aby znak ten znalazł się
pod odpowiadającym mu znakiem wzorca.
K - maksymalna ilość różnych znaków w tekście i we wzorcu (zakładamy, że występują znaki kodu ASCII)
• tworzenie tablicy przesunięć
for(i=0;i<K;i++)
shift[i]=M;
for(i=0;i<M;i++)
shift[wzor[i] ]=M-i-1;
Przykład:
wzorzec ABGBD
tekst ABCDEFGH
A B G B D
0 1 2 3 4
długość wzorca wynosi 5
Zawartość tablicy shift
k
‘A’ ‘B’ ‘C’ ‘D’ ‘E’ ‘F’ ‘G’ ‘H’
shift[k] 4
1
5
0
5
5
2
5
• algorytm BM - wykorzystanie tablicy przesunięć
a) wyznaczyć tablicę przesunięć shift dla danego wzorca
b) jeśli długość wzorca > długość tekstu – stop, nie znaleziono
c) rozpocząć przeszukiwanie od ostatniej litery wzorca i odpowiadającej jej literze tekstu i=M-1, j=M-1
d) dopóki j>=0 wykonuj
dopóki i-ty znak tekstu oraz j-ty znak wzorca różnią się, wykonuj
używając tablicy shift wyznacz przesunięcie x odpowiadające znakowi T[i]
jeśli M-j>x, zwiększ i o wartość M-j;
w przeciwnym przypadku – zwiększ i o wartość przesunięcia x
jeśli i jest większe lub równe od długości tekstu – stop, wzorca nie znaleziono
ustaw j na ostatni znak wzorca
zmniejsz i oraz j o jeden
e) odnaleziono wzorzec na pozycji i+1
for(i=M-1,j=M-1;j>=0 ; i--, j--)
while(tekst[i]!=wzor[j])
{
x=shift[tekst[i]];
if(M-j>x)
i+=M-j;
else
i+=x;
if(i>=N)
{
return -1;
}
j=M-1;
}
return i+1;
Przykład:
wzorzec: ABGBD
tekst: ABGHHABGBDEH
M=5 N=12 M ≤ N - poszukiwania można rozpocząć od i=j=4
indeks T
0 1 2 3 4 5 6 7 8 9 10 11
i
↓
tekst T
A B G H H A B G B D E H
wzorzec W A B G B D
j
↑
indeks W 0 1 2 3 4
Ponieważ j>0 i znaki H i D różnią się, więc należy wyznaczyć przesunięcie x=shift[‘H’]=5. Ponieważ
warunek 5-4>5 nie jest spełniony i=4+5=9;
indeks T
0 1 2 3 4 5 6 7 8 9 10 11
i
↓
tekst T
A B G H H A B G B D E H
wzorzec W
A B G B D
j
↑
indeks W
0 1 2 3 4
Ponieważ i-ty znak tekstu i j-ty znak wzorca są identyczne, więc należy zbadać poprzedni znak,
zmniejszając je o jeden:
indeks T
0 1 2 3 4 5 6 7 8 9 10 11
i
↓
tekst T
A B G H H A B G B D E H
wzorzec W
A B G B D
j
↑
indeks W
0 1 2 3 4
Postępując analogicznie okaże się, że wzorzec został odnaleziony.
3.
Zadania do wykonania:
Proszę napisać:
• funkcję, która znajduje długość łańcucha:
int długosc(char*lan);
• funkcję, która wypełnia tablicę przesunięć tradycyjnie nazwaną next o rozmiarze równym długości
wzorca:
int * init_next(char*wzor);
• funkcję, która wypełnia tablicę przesunięć tradycyjnie nazwaną shift o rozmiarze równym 256:
void init_shift(char*w, int*shift);
• funkcje, które znajdują jeden wzorzec w tekście, zwraca indeks lub -1 jeśli go brak :
int KMP(char* tekst, char * wzor, int *next);
int BM(char *tekst,char*wzor);
• funkcję, która znajduje wszystkie wzorce występujące w tekście, zwraca wskaźnik do tablicy
indeksów (wewnątrz funkcji dynamiczna rezerwacja pamięci dla tablicy indeksów) oraz udostępnia
ich ilość przez wskaźnik :
int * KMP_w(char* tekst, char * wzor, int * next, int * ilosc);
int* BM_w(char *tekst, char*wzor, int *ilosc);
• funkcję main, w której należy wczytać dwa dowolne łańcuchy, wywołać funkcje: init_next oraz
KMP, BM i wypisać wynik (czy znaleziono wzorzec, jeśli tak, to od jakiego indeksu się zaczyna).
Jeśli znaleziono wzorzec, należy wywołać funkcję KMP_w i również wypisać odpowiednie wyniki
(ilość znalezionych wzorców i ich indeksy). Następnie powtórzyć dla metody Boyera i Moore’a:
init_shift, BM, BM_w. Operacje: wczytania łańcuchów, wywołania funkcji i wypisywania wyników
przeprowadzać dopóki wzorzec znajduje się w tekście.

Podobne dokumenty