Neka su date niske \(T[1..n]\) i \(P[1..m]\) pri čemu je \(m \leq n\). Pretpostaviti da su svi karakteri niske \(P\) različiti. Pronaći sve validne pomeraje \(s\) niske \(P\) u tekstu \(T\), tj. pronaći sve vrednosti \(s\) za koje važi \(P[1..m] = T[s+1..s+m]\).
Sa standardnog ulaza se unose niske \(T\) i \(P\) dužine \(n\) i \(m\) (\(m \leq n\)), respektivno.
Na standardni izlaz ispisati sve validne pomeraje niske \(P\) u tekstu \(T\).
abbcabcaaabcc abc
4 9
\(i\) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
\(T\) | a | b | b | c | a | b | c | a | a | a | b | c | c |
\(P\) \((s=4)\) | a | b | c | ||||||||||
\(P\) \((s=9)\) | a | b | c |
Za sve moguće pomeraje \(s\) (\(0 \leq s \leq n - m + 1\)) proverimo da li važi \(P[1..m] = T[s+1..s+m]\). Proveru \(P[1..m] = T[s+1..s+m]\) možemo izvršiti u \(m\) koraka tako što uporedimo da li su svi karakteri \(P[i]\) i \(T[s+i]\) jednaki (\(1 \leq i \leq m\)). Ukoliko \(P[i] = T[s+i]\) za svako \(i=1 \ldots m\), znamo da \(P[1..m] = T[s+1..s+m]\), inače \(P[1..m] \neq T[s+1..s+m]\). Jasno je da nije potrebno upoređivati sve karaktere na svim pozicijama. Kada naiđemo na poziciju \(i\) za koju \(P[i] \neq T[s + i]\), pretragu možemo zaustaviti jer tada znamo da \(P[1..m] \neq T[s+1..s+m]\). Ipak vremenska složenost ove provere je \(O(m)\) u najgorem slučaju. Kako imamo ukupno \(n-m+1\) ovakvih provera, ukupna složenost algoritam je \(O((n - m + 1) m)\). Međutim, ovo složenost se dostiže samo kada \(T=a^n\) i \(P=a^m\).
Razmotrimo sada i prosečnu složenost algoritma. Neka je \(\Sigma_d = \{ 0, 1, \ldots, d-1 \}\) azbuka od \(d \geq 2\) slova. I neka je \(\Sigma_d^{*}\) skup svih mogućih konačnih niski nad azbukom \(\Sigma_d\). Neka su \(P\) i \(T\) proizvoljne niske iz \(\Sigma_d^{*}\). Izračunajmo prosečan broj upoređivanja pri proveri \(P[1..m]=T[s+1..s+m]\). Moramo uporediti karaktere \(P[1]\) i \(T[s+1]\). Karaktere \(P[2]\) i \(T[s + 2]\) upoređujemo samo ako \(P[1] = T[s+1]\). Dalje, karaktere \(P[3]\) i \(T[s+3]\) upoređujemo samo ako \(P[1] = T[s+1]\) i \(P[2] = T[s+2]\). Na kraju, karaktere \(P[m]\) i \(T[s+m]\) upoređujemo samo ako \(P[1] = T[s+1], T[2] = T[s+1], \ldots, P[m-1] = T[s+m-1]\). Zbog toga, može se pokazati da je broj upoređivanja za dati pomeraj \(s\):
\[1 + \frac{1}{d} + \frac{1}{d^2} + \ldots + \frac{1}{d^{m-1}} = \frac{1 - \frac{1}{d^m}}{1 - \frac{1}{d}}.\]
Kako je \(d \geq 2\), tada je \(1 - \frac{1}{d} \leq \frac{1}{2}\) i \(1 - \frac{1}{d^m} < 1\), te imamo da je
\[\frac{1 - \frac{1}{d^m}}{1 - \frac{1}{d}} \leq 2.\]
Zbog toga imamo najviše \(2(n-m+1)\) upoređivanja, te je prosečna složenost algoritma \(O(n)\).
#include <iostream>
#include <string>
using namespace std;
void naive_string_matcher(const string &text, const string &pattern)
{
const int n = text.size();
const int m = pattern.size();
for (int s = 0; s < n - m + 1; s++) {
// pattern[0..m-1] == text[s..s+m-1]?
for (int i = 0; i < m; i++) {
if (pattern[i] != text[s + i]) {
break;
} else if (i == m - 1){
<< s << ' ';
cout }
}
}
<< endl;
cout }
int main(void)
{
, pattern;
string text>> text >> pattern;
cin
(text, pattern);
naive_string_matcher
return 0;
}
Za bolje rešenje iskoristićemo činjenicu da su svi karakteri niske \(P\) različiti, tj. za svako \(i, j = 1, \ldots, m\), ako je \(i \neq j\) tada je \(P[i] \neq P[j]\). Naime, ukoliko postoji neko \(j \in \{ 2, \ldots, m \}\) za koje \(P[j] \neq T[s+j]\) i za svako \(i = 1, \ldots, j - 1\) važi \(P[i] = T[s+i]\), tada znamo da će za svako \(i = 1, \ldots, j - 1\), \(P[1..j - i] \neq T[s+i..s+j-1]\), te će kandidat za sledeći validni pomeraj biti \(s + j - 1\). Specijalni slučaj nastaje kada \(j = 1\) ili \(j = m + 1\), i tada će kandidat za sledeći validni pomeraj biti \(s + 1\) ili \(s + m + 1\), respektivno.
Primetimo da sada za svako \(i = 1, \ldots, n\) imamo najviše dva poređenja sa karakterom \(T[i]\). Zbog toga, ukupno imamo najviše \(2n\) poređenja, te je složenost ovog algoritma \(O(n)\).
#include <iostream>
#include <string>
using namespace std;
void all_different_matcher(const string &text, const string &pattern)
{
const int n = text.size();
const int m = pattern.size();
int s = 0;
while (s < n - m + 1) {
// pattern[0..m-1] == text[s..s+m-1] ?
for (int i = 0; i < m; i++) {
if (pattern[i] != text[s]) {
if (i == 0) {
+= 1;
s }
break;
} else if (i == m - 1){
<< s - m + 1 << ' ';
cout }
+= 1;
s }
}
<< endl;
cout }
int main(void)
{
, pattern;
string text>> text >> pattern;
cin
(text, pattern);
all_different_matcher
return 0;
}