|
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 |
©2026 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 ©2026 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.