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 wyszukać wszystkie słowa podwójne. Przez słowo podwójne (ang. square word) będziemy rozumieli łańcuch tekstowy, który możemy rozdzielić na dwa identyczne podłańcuchy. Przykład: ABBCABBC – jest słowem podwójnym zbudowanym z dwóch identycznych podsłów ABBC ABBCABBD – nie jest słowem podwójnym |
Pierwsze rozwiązanie polega na bezpośrednim sprawdzaniu każdego podsłowa
K01: m ← |s| K02: Dla i = 0, 1, …, n-2: ; przeglądamy łańcuch s wykonuj kroki K03…K07 K03: n ← 2 ; rozpoczynamy od słowa o długości 2 K04: Dopóki i = n ≤ m: wykonuj kroki K05…K07 K05: j ← i+n div 2 ; j wskazuje pierwszy znak ; drugiej połówki słowa K06: Jeśli s[i:j] = s[j:i+n], ; sprawdzamy, to pisz s[i:i+n] ; czy słowo jest podwójne K07: n ← n+2 ; przechodzimy do następnego słowa K08: 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 20 znakowy łańcuch
zbudowany ze znaków
|
Pascal// Wyszukiwanie słów podwójnych // Data: 23.07.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------- program prg; const M = 20; // długość łańcucha s var s : ansistring; i, j, n : integer; begin // generujemy łańcuch s randomize; s := ''; for i := 1 to M do s := s+chr(65+random(2)); // wypisujemy łańcuch s writeln(s); // szukamy słów podwójnych for i := 1 to M-1 do begin n := 2; while i+n <= M+1 do begin j := i+n div 2; if copy(s, i, j-i) = copy(s, j, j-i) then begin for j := 2 to i do write(' '); writeln(copy(s, i, n)); end; inc(n, 2); end; end; writeln; end. |
// Wyszukiwanie słów podwójnych // Data: 23.07.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------- #include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; int main() { const int M = 20; // długość łańcucha s string s; int i, j, n; // generujemy łańcuch s srand(time(NULL)); s = ""; for(i = 0; i < M; i++) s += char(65+rand()%2); // wypisujemy łańcuch s cout << s << endl; // szukamy słów podwójnych for(i = 0; i < M-1; i++) for(n = 2; i+n <= M; n += 2) { j = i+n/2; if(s.substr(i, j-i) == s.substr(j, j-i)) { for(j = 0; j < i; j++) cout << " "; cout << s.substr(i, n) << endl; } } cout << endl; return 0; } |
Basic' Wyszukiwanie słów podwójnych ' Data: 23.07.2008 ' (C)2020 mgr Jerzy Wałaszek '----------------------------- Const M = 20 ' długość łańcucha s Dim As String s Dim As Integer i, j, n ' generujemy łańcuch s Randomize s = "" For i = 1 To M s += Chr(65+Cint(Rnd)) Next ' wypisujemy łańcuch s Print s ' szukamy słów podwójnych For i = 1 To M-1 n = 2 While i+n <= M+1 j = i+n\2 If Mid(s, i, j-i) = Mid(s, j, j-i) Then For j = 2 To i Print " "; Next Print Mid(s, i, n) End If n += 2 Wend Next Print End |
Python
(dodatek)# Wyszukiwanie słów podwójnych # Data: 20.03.2024 # (C)2024 mgr Jerzy Wałaszek #--------------------------- import random M = 20 # długość łańcucha s # generujemy łańcuch s s = "" for i in range(M): s += chr(random.randrange(65, 67)) # wypisujemy łańcuch s print(s) # szukamy słów podwójnych for i in range(M-1): n = 2 while(i+n <= M): j = i+n//2 if s[i:j] == s[j:j+n//2]: for k in range(i): print(" ", end="") print(s[i:i+n]) n += 2 print() |
Wynik: |
BABABBABBABBABBABAAB BABA ABAB BABBAB BABBABBABBAB ABBABB ABBABBABBABB BB BBABBA BBABBABBABBA BABBAB BABBABBABBAB ABBABB BB BBABBA BABBAB ABBABB BB BBABBA BABBAB BB BABA AA |
Pierwszy algorytm posiada sześcienną klasę złożoności obliczeniowej
Jeśli wykorzystamy w odpowiedni sposób
algorytm Morrisa-Pratta, to możemy zredukować klasę
złożoności obliczeniowej do kwadratowej
Na początek rozważymy wykrywanie słów podwójnych leżących na początku
1. Długość
W tym przypadku nie można utworzyć słowa podwójnego o szerokości i.
2. Długość
Prefiks
3. Długość p
Zastanówmy się, kiedy prefiks
Symbolem A oznaczmy pokrywający się fragment
aaBa ≠ aBaa, jeśli B ≠ Ø
Sytuacja taka wystąpi, gdy szerokość pokrywającego się fragmentu
A będzie dokładnie równa
Ostatni przypadek wystąpi, gdy zachodzi nierówność:
Na początku prefiksu pozostaje fragment B. Następnie mamy
pokrywający się fragment A i na końcu sufiksu pozostaje fragment
C o tej samej
długości co fragment B. Pokrywający się fragment
A musi być słowem podwójnym – dzielimy go zatem na dwie połówki oznaczone małymi
Ba = aC
Przyrównujemy do siebie prefiks i sufiks
Pozostaje jedynie problem sprawdzenia, czy fragment
A jest słowem
podwójnym. Zwróć jednakże uwagę, iż jest on zawsze krótszy od prefiksu
Podsumowując stwierdzamy:
Wyszukanie wszystkich słów podwójnych w łańcuchu s sprowadza się do wyszukiwania tych słów w kolejnych sufiksach poczynając od niewłaściwego (obejmującego cały łańcuch) i kończąc na sufiksie dwuznakowym. Algorytm MP działa w czasie liniowym, natomiast operacja przeszukania wszystkich sufiksów łańcucha s będzie wymagała kwadratowej złożoności obliczeniowej. Złożoność pamięciowa algorytmu jest liniowa.
K01: n ← |s| ; wyznaczamy długość łańcucha s K02: Dla j = 0, 1, …, n-1: ; przeglądamy sufiksy s[j:n] wykonuj kroki K03…K17 K03: Π[0] ← -1 ; algorytmem MP wyznaczamy kolejne K04: b ← -1 ; maksymalne prefikso-sufiksy dla K05: Dla i = 1, 2, …, n-j: ; danego sufiksu s[j:n] wykonuj kroki K06…K17 K06: Dopóki (b > -1) ∧ (s[j+b] ≠ s[j+i-1]): wykonuj b ← Π[b] K07: b ← b+1 K08: Π[i] ← b K09: b2 ← b shr 1 ; podwójna szerokość prefikso-sufiksu K10: Jeśli i nieparzyste, ; słowo podwójne posiada to następny obieg pętli K05 ; parzystą liczbę liter K11: P[i shr 1] ← false ; inicjujemy element tablicy P K12: Jeśli b2 < i, ; przypadek nr 1 to następny obieg pętli K05 K13: Jeśli b2 = i, ; przypadek nr 2 to idź do kroku K16 K14: Jeśli b2+b < i shl 1, ; przypadek nr 3 z B ≠ Ø to następny obieg pętli K05 K15: Jeśli P[(b2-i) shr 1] = false, ; przypadek nr 3 to następny obieg pętli K05 ;-obszar wspólny nie ; jest słowem podwójnym K16: P[i shr 1] ← true; ; zapamiętujemy, iż s[j:j+i] ; jest słowem podwójnym K17: Pisz s[j:j+i] ; wyprowadzamy znalezione słowo podwójne K18: 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 20 znakowy łańcuch
zbudowany ze znaków
|
Pascal// Wyszukiwanie słów podwójnych // Data: 9.08.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------- program prg; const N = 20; // długość łańcucha s var s : ansistring; i, j, k, b, b2 : integer; PI : array [0..N] of integer; P : array [0..N shr 1] of boolean; begin // generujemy łańcuch s randomize; s := ''; for i := 1 to N do s := s+chr(65+random(2)); // wypisujemy łańcuch s writeln(s); // szukamy słów podwójnych for j := 1 to N-1 do begin PI[0] := -1; b := -1; for i := 1 to N-j+1 do begin while(b > -1) and (s[j+b] <> s[j+i-1]) do b := PI[b]; inc(b); PI[i] := b; b2 := b shl 1; if i and 1 = 0 then begin P[i shr 1] := false; if(b2 < i) then continue; if(b2 > i) then begin if b2+b < (i shl 1) then continue; if not (P[(b2-i) shr 1]) then continue; end; P[i shr 1] := true; for k := 2 to j do write(' '); writeln(copy(s, j, i)); end; end; end; writeln; end. |
// Wyszukiwanie słów podwójnych // Data: 9.08.2008 // (C)2020 mgr Jerzy Wałaszek //----------------------------- #include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; const int N = 20; // długość łańcucha s int main() { string s; int i, j, k, b, b2, PI[N+1]; bool P[(N >> 1)+1]; // generujemy łańcuch s srand(time(NULL)); s = ""; for(i = 0; i < N; i++) s += char(65+rand() % 2); // wypisujemy łańcuch s cout << s << endl; // szukamy słów podwójnych for(j = 0; j < N-1; j++) { PI[0] = b = -1; for(i = 1; i <= N-j; i++) { while((b > -1) && (s[j+b] != s[j+i-1])) b = PI[b]; PI[i] = ++b; b2 = b << 1; if(!(i & 1)) { P[i >> 1] = false; if(b2 < i) continue; if(b2 > i) { if(b2+b < (i << 1)) continue; if(!P[(b2-i) >> 1]) continue; } P[i >> 1] = true; for(k = 0; k < j; k++) cout << " "; cout << s.substr(j, i) << endl; } } } cout << endl; return 0; } |
Basic' Wyszukiwanie słów podwójnych ' Data: 9.08.2008 ' (C)2020 mgr Jerzy Wałaszek '----------------------------- Const N = 20 ' długość łańcucha s Dim As String s Dim As Integer i, j, k, b, b2, PI(N) Dim As Byte P(N Shr 1) ' generujemy łańcuch s Randomize s = "" For i = 1 To N s += Chr(65+Cint(Rnd)) Next ' wypisujemy łańcuch s Print s ' szukamy słów podwójnych For j = 1 To N-1 PI(0) = -1: b = -1 For i = 1 To N-j+1 While (b > -1) And _ (Mid(s, j+b, 1) <> Mid(s, j+i-1, 1)) b = PI(b) Wend b += 1: PI(i) = b: b2 = b Shl 1 if(i And 1) = 0 Then P(i Shr 1) = 0 if(b2 < i) Then Continue For if(b2 > i) Then If b2+b < (i Shl 1) Then Continue For If P((b2-i) Shr 1) = 0 Then _ Continue For End If P(i Shr 1) = 1 For k = 2 To j Print " "; Next Print Mid(s, j, i) End If Next Next Print End |
Python
(dodatek)# Wyszukiwanie słów podwójnych # Data: 21.03.2024 # (C)2024 mgr Jerzy Wałaszek #----------------------------- import random N = 20 # długość łańcucha s # tworzymy tablice t_pi = [0] * (N+1) t_p = [False] * ((N >> 1)+16) # generujemy łańcuch s s = "" for i in range(N): s += chr(random.randrange(65, 67)) # wypisujemy łańcuch s print(s) # szukamy słów podwójnych for j in range(N-1): t_pi[0] = -1 b = -1 for i in range(1, N-j+1): while (b > -1) and \ (s[j+b] != s[j+i-1]): b = t_pi[b] b += 1 t_pi[i] = b b2 = b << 1 if not (i & 1): t_p[i >> 1] = False if b2 < i: continue if b2 > i: if b2+b < i << 1: continue if not t_p[(b2-i) >> 1]: continue t_p[i >> 1] = True for k in range(j): print(" ", end="") print(s[j:j+i]) print() |
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.