Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek |
ProblemW łańcuchu s należy wyznaczyć wszystkie
wystąpienia |
Profesor Donald Knuth przeanalizował dokładnie
algorytm
Morrisa-Pratta i zauważył, iż można go wciąż
ulepszyć. Ulepszenie to polega na nieco innym sposobie wyznaczania
W celu uniknięcia po przesunięciu okna wzorca natychmiastowej niezgodności
musimy dodatkowo zażądać, aby
Tablicę szerokości
Przykład:
Wyznaczymy tablicę KMPNext dla wzorca ABACABAB – identyczny wzorzec jak w rozdziale o tworzeniu tablicy Π.
Lp. | Tworzona tablica Next | Opis |
1. |
s : A B A C A B A B i : |
Element KMPNext[0] jest zawsze upraszcza się algorytm wyznaczania kolejnych elementów tablicy. |
2. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0 ? ? ? ? ? ? ? |
Prefiks jednoznakowy
A
posiada zawsze wpisujemy do tablicy. |
3. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 ? ? ? ? ? ? |
Prefikso-sufiks prefiksu dwuznakowego
AB ma za leżący tuż za prefiksem jest tym samym znakiem. Skoro tak, to p istnieje |
4. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1 ? ? ? ? ? |
Prefikso-sufiks prefiksu
ABA ma szerokość 1 (obejmuje pierwszą i ostatnią literkę A). Znak B leżący tuż za |
5. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1-1 ? ? ? ? |
Prefiks
ABAC ma w tablicy |
6. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1-1 0 ? ? ? |
Prefiks
ABACA
posiada Jednakże występuje ten sam szerokość następnego KMPNext[1] = 0. W tym przypadku Jest to zatem poszukiwany prefikso-sufiks i szerokość 0 wpisujemy do tablicy. |
7. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1-1 0-1 ? ? |
Prefiks
ABACAB
posiada o szerokości 2 sam następnego Prefikso-sufiks umieszczamy -1. |
8. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1-1 0-1 3 ? |
Prefiks
ABACABA
posiada prefikso-sufiks o szerokości 3 |
9. |
s : A B A C A B A B i : 0 1 2 3 4 5 6 7 8 KMPNext[]:-1 0-1 1-1 0-1 3 2 |
Cały wzorzec ABACABAB posiada
możliwości sprawdzenia znaku A leżącego tuż prefikso-sufikse to w tablicy gotowa. |
Porównajmy tablicę Π z tablicą KMPNext:
wzorzec | A | B | A | C | A | B | A | B | |
indeks | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Π | -1 | 0 | 0 | 1 | 0 | 1 | 2 | 3 | 2 |
KMPNext | -1 | 0 | -1 | 1 | -1 | 0 | -1 | 3 | 2 |
Algorytm KMP wyszukiwania wzorca wykorzystujący tablicę KMPNext jest
identyczny z algorytmem MP wykorzystującym tablicę
Π. W algorytmie KMP zastępujemy jedynie odwołania do tablicy
Π odwołaniami do tablicy KMPNext, która pozwala bardziej efektywnie wyszukiwać wystąpienia wzorców, ponieważ
pomijane są puste przebiegi. Klasa złożoności obliczeniowej jest równa O(m+n), gdzie m jest długością
K01: KMPNext[0] ← -1 ; na początku tablicy KMPNext ; wstawiamy wartownika K02: b ← -1 ; początkowo długość prefikso-sufiksu ; ustawiamy na -1 K03: Dla i = 1, 2, …, |s|: ; wyznaczamy długości wykonuj kroki K04…K06 ; prefikso-sufiksów ; dla prefiksów łańcucha s K04: Dopóki (b > -1) (s[b] ≠ s[i-1]): ; w pętli K04 wykonuj b ← KMPNext[b] ; szukamy prefikso-sufiksu b ; prefiksu wzorca, który da się rozszerzyć. ; Z pętli wychodzimy, jeśli natrafimy na wartownika ; lub prefikso-sufiks jest rozszerzalny. K05: b ← b+1 ; rozszerzamy prefikso-sufiks. K06: Jeśli i = |s| s[i] ≠ s[b], ; Jeśli następny znak to KMPNext[i] ← b, ; wzorca różni się od znaku tuż inaczej KMPNext[i] ← KMPNext[b] ; za prefikso-sufiksem, ; to w tablicy KMPNext umieszczamy szerokość ; prefikso-sufiksu. Inaczej w KMPNext umieszczamy ; zredukowaną szerokość prefikso-sufiksu K07: Zakończ
K01: Wyznacz tablicę KMPNext ; wyznaczamy tablicę maksymalnych ; prefikso-sufiksów algorytmem KMP. K02: pp ← -1 ; wstępna pozycja wzorca ustawiona na -1. K03: b ← 0 ; wstępną długość prefikso-sufiksu ustawiamy na 0. K04: Dla i = 0, 1, …|s|-1: ; pętla wyznacza znaki łańcucha do wykonuj kroki K05…K10 ; porównania ze znakami wzorca. K05: Dopóki (b > -1) (p[b] ≠ s[i]): ; Szukamy we wzorcu p wykonuj b ← KMPNext[b] ; rozszerzalnego prefiksu pasującego ; do prefiksu okna wzorca. Pętlę K05 przerywamy, jeśli ; znajdziemy rozszerzalny prefiks lub napotkamy wartownika -1. K06: b ← b+1 ; Rozszerzamy prefiks o 1 znak. Jeśli prefiks był ; wartownikiem, to otrzymuje wartość 0. K07: Jeśli b < |p|, ; Jeśli prefiks nie obejmuje całego wzorca, to następny obieg pętli K04 ; to kontynuujemy pętlę K04, ; czyli porównujemy znak p[b] z kolejnym znakiem łańcucha s. K08: pp ← i-b+1 ; wyznaczamy pozycję wzorca p w łańcuchu s. K09: Pisz pp ; wyprowadzamy tę pozycję. K10: b ← KMPNext[b] ; redukujemy b do długości prefikso-sufiksu ; wzorca. K11: Jeśli pp = -1, ; jeśli wzorzec p nie występuje w s, to pisz -1 ; to wyprowadzamy -1. K12: Zakończ
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
Program generuje 79 znakowy łańcuch zbudowany z pseudolosowych kombinacji liter A i B oraz 5 znakowy wzorzec wg tego samego schematu. Następnie program wyznacza tablicę długości maksymalnych prefikso-sufiksów kolejnych prefiksów wzorca i wykorzystuje ją do znalezienia wszystkich wystąpień wzorca w łańcuchu. |
Pascal// Wyszukiwanie wzorca algorytmem KMP // Data: 4.06.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------------- program prg; const N = 79; // długość łańcucha s M = 5; // długość wzorca p var s, p : ansistring; KMPNext : array [0..M] of integer; i, b, pp : integer; begin randomize; // generujemy łańcuch s s := ''; for i := 1 to N do s := s+chr(65+random(2)); // generujemy wzorzec p := ''; for i := 1 to M do p := p+chr(65+random(2)); // obliczamy tablicę KMPNext KMPNext[0] := -1; b := -1; for i := 1 to M do begin while(b > -1) and (p[b+1] <> p[i]) do b := KMPNext[b]; inc(b); if(i = M) or (p[i+1] <> p[b+1]) then KMPNext[i] := b else KMPNext[i] := KMPNext[b]; end; // wypisujemy wzorzec p writeln(p); // wypisujemy łańcuch s writeln(s); // poszukujemy pozycji wzorca w łańcuchu pp := 0; b := 0; for i := 1 to N do begin while(b > -1) and (p[b+1] <> s[i]) do b := KMPNext[b]; inc(b); if b = M then begin while pp < i-b do begin write(' '); inc(pp); end; write ('^'); inc(pp); b := KMPNext[b]; end end; writeln; end. |
// Wyszukiwanie wzorca algorytmem KMP // Data: 4.06.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------------- #include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; const int N = 79; // długość łańcucha s const int M = 5; // długość wzorca p int main() { string s, p; int KMPNext[M+1], i, b, pp; srand(time(NULL)); // generujemy łańcuch s s = ""; for(i = 0; i < N; i++) s += 65+rand() % 2; // generujemy wzorzec p = ""; for(i = 0; i < M; i++) p += 65+rand() % 2; // obliczamy tablicę KMPNext KMPNext[0] = b = -1; for(i = 1; i <= M; i++) { while((b > -1) && (p[b] != p[i-1])) b = KMPNext[b]; ++b; if((i == M) || (p[i] != p[b])) KMPNext[i] = b; else KMPNext[i] = KMPNext[b]; } // wypisujemy wzorzec cout << p << endl; // wypisujemy łańcuch s cout << s << endl; // poszukujemy pozycji wzorca w łańcuchu pp = b = 0; for(i = 0; i < N; i++) { while((b > -1) && (p[b] != s[i])) b = KMPNext[b]; if(++b == M) { while(pp < i-b+1) { cout << " "; pp++; } cout << "^"; pp++; b = KMPNext[b]; } } cout << endl; return 0; } |
Basic' Wyszukiwanie wzorca algorytmem KMP ' Data: 4.06.2008 ' (C)2020 mgr Jerzy Wałaszek '----------------------------------- Const N = 79 ' długość łańcucha s Const M = 5 ' długość wzorca p Dim As String s, p Dim As Integer KMPNext(M), i, b, pp Randomize ' generujemy łańcuch s s = "" For i = 1 To N s += Chr(65+Cint(Rnd)) Next ' generujemy wzorzec p = "" For i = 1 To M p += Chr(65+Cint(Rnd)) Next ' obliczamy tablicę KMPNext KMPNext(0) = -1: b = -1 For i = 1 To M While (b > -1) And _ (Mid(p, b+1, 1) <> Mid(p, i, 1)) b = KMPNext(b) Wend b += 1: if (i = M) Or _ (Mid(p, i+1, 1) <> Mid(p, b+1, 1)) Then KMPNext(i) = b Else KMPNext(i) = KMPNext(b) End If Next ' wypisujemy wzorzec p Print p ' wypisujemy łańcuch s Print s ' poszukujemy pozycji wzorca w łańcuchu pp = 0: b = 0 For i = 1 To N While (b > -1) And _ (Mid(p, b+1, 1) <> Mid(s, i, 1)) b = KMPNext(b) Wend b += 1 If b = M Then While pp < i-b Print " "; pp += 1 Wend Print "^"; pp += 1 b = KMPNext(b) End If Next Print End |
Python
(dodatek)# Wyszukiwanie wzorca algorytmem KMP # Data: 8.03.2024 # (C)2024 mgr Jerzy Wałaszek #----------------------------------- import random N = 79 # długość łańcucha s M = 5 # długość wzorca p # generujemy łańcuch s s = "" for i in range(N): s += chr(random.randrange(65, 67)) # generujemy wzorzec p p = "" for i in range(M): p += chr(random.randrange(65, 67)) # obliczamy tablicę KMPNext kmp_next = [-1] b = -1 for i in range(1, M+1): while (b > -1) and (p[b] != p[i-1]): b = kmp_next[b] b += 1 if(i == M) or (p[i] != p[b]): kmp_next.append(b) else: kmp_next.append(kmp_next[b]) # wypisujemy wzorzec p print(p) # wypisujemy łańcuch s print(s) # poszukujemy pozycji wzorca w łańcuchu pp = 0 b = 0 for i in range(N): while (b > -1) and (p[b] != s[i]): b = kmp_next[b] b += 1 if b == M: while pp < i-b+1: print(" ", end="") pp += 1 print("^", end="") pp += 1 b = kmp_next[b] print() print() |
Wynik: |
BBBAB BAAABBBABBAABAAABAAAABBBBAABABAABBABBBABAABABAAABBAABBABBAABABAABBBABBAAAAAAAAA ^ ^ ^ |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.