Sunteți pe pagina 1din 8

Potrivirea șirurilor (cutarea apariției unui subșir(model-pattern) într-un șir(text))

1. Introducere (punerea problemei)

Vom numi subșirul a cărui apariție se caută model iar șirul în care se caută text.
Fie
T[1..n] – textul de lungime n
P[1..m] – modelul de lungime m, mn
Textul și modelul sunt, de obicei șiruri de caractere.

Definiție. Spunem căP apare cu deplasamentuls înT dacă:


1. 0 s n-m
2. T[s+j]=P[j], j, 1 j m
Problemă.Să se determine toate deplasamenteles cu careP apare înT.
Exemplu. Fie T abcabaabcabac și P abaa. Pentru s=3 se obține
abcabaabcabac
s=3
abaa

2. Algoritmul naiv (brute-force)

Sunt generate pe rând toate deplasamentele posibile (între 0 și n – m + 1) și pentru fiecare deplasament sunt verificate
m caractere din text.

pentru s  0, n – m + 1 executa
ok  1
pentru i  0, m – 1 executa
daca t[s + i] ≠ p[i] atunci
ok  0
sf. daca
sf. pentru
daca ok = 1 atunci
scrie s
sf. daca
sf. pentru
Complexitate O(m * (n – m + 1))

3. Algoritmul Knuth – Morris - Pratt (KMP)

3.1. Prezentarea funcționării

Algoritmul se bazează pe compararea caracterelor din text cu caracterele din model (ca și algoritmul naiv), dar
folosește comparațiile făcută pentru a determina care este următorul deplasament de la care merită să fie făcute noi
comparații. Să presupunem că la un moment dat, pentru un deplasament oarecare, am găsit că k caractere din text
corespund cu primele k caractere din model (T[i … i + k - 1] = P[1 … k]). După ce am investit timp în găsirea acestei
potriviri parțiale putem folosi aceste date în două moduri:
- să sărim peste câteva deplasamente pentru care potrivirea totală nu e posibilă;
- după ce am potrivit modelul într-un deplasament posibil să sărim peste comparația unor caractere care știm că
vor coincide.
Fie T = xyxxyxyxyyxyxyxyyxyxyxxy și P = xyxyyxyxyxx
Pentru început vom alinia modelul la începutul textului și vom începe să comparăm caracter cu caracter:

Observăm că P[1 . . 3] corespunde cu T[1 . . 3] și găsim o nepotrivire pentru P[4] ≠ T[4]. Bazându-ne pe faptul că
P[1 . . 3] = T[1. . 3] și ignorând simbolurile din text și model de după poziția 3, care este următorul deplasament
pentru care putem avea o corespondență (fie ea și parțială)? Observăm că nu are rost să utilizăm deplasamentul 2, vom
alinia modelul în poziția 3. Dar în momentul alinierii știm că P[1] = T[3], așa că următoarea comparație va fi între
P[2] și T[4].

Vom alinia modelul în poziția 3 și vom compara pe P[2] cu T[4].

Observăm că P[4] ≠ T[2] așa că, ignorând caracterele de după T[4] următorul deplasament posibil apare în poziția 4.

Aliniem modelul în poziția 4 și începem să comparăm caracter cu caracter.

Observăm că P[1 .. 4] corespunde cu T[4 .. 7] și apare o nepotrivire pentru P[5] ≠ T[8]. Ignorând caracterele din text
de după poziția 7 și din model de după poziția 4, care este următorul deplasament pentru care poate exista o
corespondență? Un deplasament posibil este poziția 6, potrivim modelul în poziția 6, dar în momentul potrivirii știm
că P[1 . . 2] = T[6. . 7] așa că vom începe compararea lui P[3] și T[8].

Aliniem modelul în poziția 6 și începem să comparăm caracter cu caracter P[3] = T[8], P[4] = T[9] … P[10] = T[15].

Observăm că P[1 . . 10] = T[6 . . 15] și că prima nepotrivire descoperită e pentru P[11] ≠ T[16]. Ignorând
caracterele din text de după poziția 15 și din model de după poziția 10, care este următorul deplasament pentru care
poate apărea o potrivire? Observăm că posibilul deplasament este 13, așezăm modelul în poziția 13, dar în acest
moment știm că P[1 . . 3] = T[13 . . 15].

Aliniem modelul în poziția 13, știm că P[1 . . 3] = T[13 . . 15], com compara P[4] cu T[16], P[5] cu T[17] etc..

Se găsește o potrivire totală pentru deplasamentul 13. Următoarea posibilă potrivire ar fi pentru deplasamentul 23, dar
aceasta nu mai poate conține o potrivire, așa că algoritmul se oprește.

3.2. Regula de determinare a următorului deplasament posibil


Pentru a determina peste câte poziții putem sări în determinarea următorului deplasament posibil vom face
următoarele precizări.
Fie șirul S = c1c2c3 … cn
- numim prefix al șirului S orice șir de forma c1c2c3 … ck unde 1 ≤ k ≤ n (șirul vid e considerat prefix, dar nu e
considerat prefix propriu. Șirul inițial e prefix, dar nu e prefix propriu).
- numim sufix al șirului S orice șir de forma ckck+1ck+2 … cn unde 1 ≤ k ≤ n (șirul vid e considerat sufix, dar nu e
considerat sufix propriu. Șirul inițial e sufix, dar nu e sufix propriu).
Să presupunem că primele k caractere din model corespund cu k caractere din text P[1 . . k] = T[i – k + 1 . . i]
(potrivirea se termină în poziția i) și avem o nepotrivire pentru P[k+1] ≠T[i+1]. Modelul va trebui deplasat spre
dreapta a.î cel mai lung prefix propriu al lui P[1 . . . k] care este și sufix pentru P[1 . . . k] să fie aliniat cu textul iar
ultimul caracter din acest prefix să fie aliniat cu T[i].

Să notăm cu π(k) valoarea pentru care P[1, . . . , π(k)] este cel mai lung prefix propriu al lui P[1 . . k] care este și sufix
al acestui șir. În acest caz modelul se mută astfel încât P[1, . . . , π(k)] să fie aliniat cu T[i − π(k) + 1, . . . , i]
Algoritmul KMP determină pentru fiecare k, care este lungimea celui mai lung prefix propriu al lui P, care este și
sufix al acestuia. Valoarea π(k) se numește funcție prefix. Funcția este definită printr-un tablou cu m elemente, tablou
care reține în elementul de indice k o valoare care indică cel mai lung prefix propriu al lui P[1 . . k] care este și sufix.
k 1 2 3 4 5 6 7 8 9 10 11
P x y x y y x y x y x x
π(k) 0 0 1 2 0 1 2 3 4 3 1

În concluzie, dacă P[1, . . . , k] se potrivește cu T[i − k + 1, . . . , i], algoritmul KMP va compara pe P[k+1] cu T[i+1]
și va face unul din următorii pași:

- dacă P[k+1] = T[i+1] lungimea subșirului care corespunde se extinde (dacă k + 1 == m am găsit întregul
model în text).
- dacă P[k+1] ≠ T[i+1], modelul se mută spre dreapta cu k - π(k) poziții.

Algoritmul va continua până când sfârșitul textului este atins.

3.3. Determinarea funcției prefix

Pentru determinarea valorilor lui π(k) algoritmul KMP va face o preprocesare. Se va suprapune modelul cu el insusi.
Ne vom folosi de următoarele observații:
- valoarea lui π(1) = 0 (cel mai lung prefix propriu, adică de o lungime strict mai mică decât lungimea șirului
inițial este 0).
- dacă P[1, . . . , π(k)] este cel mai lung prefix propriu al lui P[1 . . k] care este și sufix al acestuia, atunci P[1, .
. . , π(k-1)] este cel mai lung prefix propriu al lui P[1 . . k-1].

Să presupunem că am determinat π(i) și vrem să determinăm π(i+1). Sa notam k = π(i). Verificăm dacă P[k +
1] se potriveste cu P[i + 1]. În caz afirmativ π(i+1) = k + 1. În caz contrar căutăm cel mai lung prefix care e și sufix la
care putem adăuga caracterul de pe poziția i + 1.

În prezentare, pentru simplificare, am considerat că indexarea începe de la valoarea 1. În pseudocoduri voi


considera că indexarea șirurilor începe cu poziția 0 (proprie limbajului C/C++)

//pi[i] – lungimea celui mai lung prefix propriu care e si sufix pt P[0..i]
n  lungime(T), m  lungime (P)
pi[0]  0 //totdeauna
k  0 // k = lungimea celui mai lung prefix propriu care e si suffix
pentru i  1, m – 1 executa //pentru celelalte valori ale fct prefix
//consideram pi[i-1] cunoscuta si vrem sa determinam pi[i]
//cautam care este cel mai lung prefix la care putem adauga P[i]
//a.i. prin adaugarea caracterului prefixul sa fie si sufix
cat timp k > 0 si P[i] ≠ P[k] executa
k  pi [k – 1] //prefixul prefixului
sf. cat timp

daca P[k] = P[i] atunci //daca il pot adauga la prefixul de lung k


k  k + 1 //il adaug
sf. daca

pi[i]  k //retin cmlpp care e si suffix pentru P[0 .. i]


sf. pentru

3.4. Algoritmul în pseudocod

a) Cormen – Leirson - Rivest


n  lungime(T), m  lungime (P)
pi  determinare_fct_prefix
k  0 // k = lungimea celui mai lung prefix propriu care e si suffix
pentru i  1, n – 1 executa
//se compara T[i] cu P[k]
//in caz de egalitate se lungeste potrivirea
//in caz contrar se cauta noul deplasament
cat timp k > 0 si T[i] ≠ P[k] executa
k  pi [k – 1]
sf. cat timp

daca P[k] = T[i] atunci //in caz de potrivire


k  k + 1 //lungesc potrivirea
sf. daca

daca k = m atunci //daca am potrivire completa


scrie “deplasament:”, i – m + 1
k <- pi[k – 1]
sf. daca
sf. pentru

b) Varianta proprie
n  lungime(T), m  lungime (P)
pi  determinare_fct_prefix
k  0 depl  0, i  0, déjà_testate  0
cat timp i < n executa

i  depl + déjà_testate //de aici incep sa verific in text potrivirea


j  déjà_testate//de aici incep sa verific in model potrivirea
k  déjà_testate//se potrivesc déjà, fac parte din prefix

cat timp i<n si j<m si t[i] = p[j] executa //cat timp am potrivire
i  i + 1, j  j + 1, k  k + 1 //numar si trec la urm. caracter
sf. cat timp

daca k = m atunci
scrie “deplasament:”, depl
sf. daca

daca k = 0 atunci //daca nu am nicio potrivire


depl  depl + 1 //incerc cu urm deplasament
déjà_testate  0
altfel
depl  depl + k – pi[k-1] //calculez noul deplasament
déjà_testate  pi[k-1] //caracterele din prefix se potrivesc
sf. daca
sf. cat timp
Potrivirea sirurilor
http://www.infoarena.ro/problema/strmatch

Se dau doua siruri A si B formate din litere mici si mari ale alfabetului englez si din cifre. Se cere gasirea tuturor
aparitiilor sirului A ca subsecventa a sirului B.

Date de intrare
Fisierul de intrare strmatch.in contine 2 linii cu sirurile A, respectiv B.

Date de iesire
In fisierul de iesire strmatch.out se va afla pe prima linie numarul N de aparitii a sirului A in sirul B. Pe
urmatoarea linie se vor afla N numere care reprezinta pozitiile in care sirul A se potriveste peste sirul B, afisate in
ordine crescatoare. Pentru a evita fisierele de output foarte mari, in cazul in care N este mai mare decat 1000 se
vor afisa doar primele 1000 de pozitii. Sirurile sunt indexate de la 0.

Restrictii
 Lungimea sirurilor A si B se afla in intervalul [1, 2 000 000]

Exemplu
strmatch.in strmatch.out

ABA 2

CABBCABABAB 5 7

Explicatie

Cele doua aparitii sunt:

 CABBCABABAB
 CABBCABABAB

1423. String Tale


http://acm.timus.ru/problem.aspx?space=1&num=1423

Background
I want to tell you a story. Not entirely, but only the very beginning, because the ending of this story became a legend
of programming—as well as its heroes.

When computers were large, when trees were small, when the sun shined brighter… Once upon a time there were
Three Programmers. I doubt whether they participated in any programming contests, because there were no
contests at that ancient time. There was neither ACM ICPC nor Timus Online Judge. But there were Three
Programmers.

Problem
One day Three Programmers invented an amusing game to train memory and mental faculties. The First
Programmer thought out a string S which was N characters long and passed it to the Second and the Third
Programmers. The Second Programmer executed X (0 ≤ X < N) successive cycle shifts (a cycle shift is a transfer of the
last character of the string to the beginning of this string) with this string. As a result of these operations a string T
was produced, and the Second Programmer passed it to the Third Programmer. A task of the Third Programmer was
to find the number X or make sure that the Second Programmer was mistaken, because the string T could not be
produced from the string S via cycle shifts.

Input
The first line contains the integer number N (1 ≤ N ≤ 250000). The second line contains the string S. The third line
contains the string T. Each string has length N and may contain any ASCII characters with codes from 33 to 255.

Output
If the string T can be produced from the string S via cycle shifts you should output the desired number X, otherwise
you should output “−1”. If the problem has several solutions, you may output any of them.

Sample
input output

11 9
abracadabra
racadabraab

1354. Palindrome. Again Palindrome


http://acm.timus.ru/problem.aspx?space=1&num=1354
(nu neaparat KMP, dar siruri de caractre)
A word is the nonempty sequence of symbols a1a2…an. A palindrome is the word a1a2…an that is read from the left to
the right and from the right to the left the same way (a1a2…an = anan−1…a1). If S1 = a1a2…an and S2 = b1b2…bm,
then S1S2= a1a2…anb1b2…bm. The input contains some word S1. You are to find a nonempty word S2 of the minimal
length thatS1S2 is a palindrome.

Input
The first input line contains S1 (it may consist only of the Latin letters). It’s guaranteed that the length of S1 doesn’t
exceed 10000 symbols.

Output
S1S2.

Samples
input output

No NoN
OnLine OnLineniLnO
AbabaAab AbabaAababA

10298. Power Strings

Given two strings a and b we define a * b to be their concatenation. For example, if a = `abc' and b = `def'
then a * b = `abcdef'. If we think of concatenation as multiplication, exponentiation by a non-negative
integer is defined in the normal way: a0 = “” (the empty string) and a(n+1) = a * (an).
Input
Each test case is a line of input representing s, a string of printable characters. The length of s will be at
least 1 and will not exceed 1 million characters. A line containing a period follows the last test case.
Output
For each s you should print the largest n such that s = an for some string a.
Sample Input
abcd
aaaa
ababab
.
Sample Output
1
4
3

S-ar putea să vă placă și