====== IN17 Algoritmy pro práci s řetězci ======
(Vyhledávání vzorků v textu. Naivní vyhledávání. Algoritmus Karpův-Rabinův. Algoritmus založený na konečných automatech. Složitost vyhledávání.)
===== Vypracovanie =====
==== Vyhľadávanie vzorky v texte ====
Je daný text //T// a vzorka (alebo //pattern//) //P// - reťazce nad abecedou ∑. Úlohou je vyhľadať všetky výskyty vzorky v texte. Text je daný ako pole //T[1..n]//, vzorka ako //P[1..m]//.
Hovoríme, že vzorka //P// sa vyskytuje v texte //T// s posunom //s// ak a (t.j. ). Číslo //s// uvedených vlastností sa nazýva **platným posunom** pre text //T// a vzorku //P//.
Problém vyhľadania vzorky môžeme formulovať ako úlohu nájsť pre dané //T// a //P// všetky platné posuny. Na riešenie tohto problému môžeme použiť rôzne algoritmy, ktoré sa líšia zložitosťou predspracovania textu a samotného vyhľadávania:
*naivné vyhľadávanie
*Karp-Rabinov algoritmus
*algoritmus založený na konečných automatoch
*algoritmus Knuth-Morris-Pratt
*algoritmus Boyer-Moore
==== Naivné vyhľadávanie ====
NAIVNE_VYHLADAVANIE(T,P)
for s = 0 to n-m do
if P[1..m] = T[s+1..s+m]
then print "s je platny posun" fi
od
Ide o naivné vyhľadávanie: vzorku //P// postupne "prikladáme" (posúvame o jedno miesto doprava v texte) k textu //T// a porovnávame jednotlivé písmená až kým sa vzorka nedá už posunúť (jej prvé písmeno je na pozícii //n-m// v texte).
**Zložitosť:** cyklus sa vykoná **n-m+1** krát, v každom cykle sa vykoná **m** porovnaní. Spolu teda . Iba v najhoršom prípade (vzorka a príslušná časť textu sa líšia len v poslednom písmene) sa vykoná //m// porovnaní (v najlepšom prípade len 1 porovnanie).
==== Karp-Rabinov algoritmus ====
Karp-Rabinov algoritmus využíva naivné vyhľadávanie, ale zameriava sa na zrýchlenie porovnávania vzorky s časťou textu (časť algoritmu ), používa na to hašovaciu funkciu.
Predpokladajme, že . Každý reťazec nad abecedou môžeme chápať ako číslo zapísané v desiatkovej sústave. Označme //p// číslo zodpovedajúce reťazcu a //ts// čísla zodpovedajúce reťazcom . Problém overiť, či //s// je platným posunom sa redukuje na problém overiť, či //ts = p//.
KARP_RABIN(T[1..n],P[1..m])
hpat := hash(P[1..m]) //predspracovanie
for s = 0 to n-m do
hs := hash(T[s+1..s+m])
if (hs == hpat)
if P[1..m] = T[s+1..s+m]
then print "s je platny posun" fi
fi
od
Najskôr si predspracujeme vzorku (vypočítame jej hash), v našom prípade pomocou Hornerovej schémy (ak a=1, b=2, c=3, cast textu 'ab' = 12, 'bc' = 23) v čase . Podobne spočítame hash v čase . Trik Karp-Rabinovho algoritmu spočina v tom, že hash ďalšej časti textu (//T[2..m+1]// atď.) môžeme vypočítať z predchádazjúceho hasha v konštantnom čase. Zvyšné hashe (//t1..tn-m// teda spočítame v čase .
Ak sú čísla //p,ts// príliš veľké, používa sa výpočet modulo //q//, kde typicky //q// je prvočíslo také, že 10q ≈ počítačové slovo. Test //ts = p// sa nahradí testom . Číslo //s//, pre ktoré platí uvedená rovnosť je len potenciálnym posunom, jeho platnosť sa musí overiť porovnaním príslušných reťazcov.
**Zložitosť predspracovania** je , **zložitosť výpočtu** v najhoršom prípade (t.j ak pre všetky //s// platí skúmaná rovnosť) je . Zložitosť konkrétneho výpočtu je daná počtom platných posunov. Ak očakávaný počet platných posunov je //c//, tak očakávaná zložitosť algoritmu je v .
Algoritmus Karp-Rabin je vhodné použiť, ak v texte hľadáme celé vety (neočakávame, že výskytov bude veľa), v tom prípade algoritmus dosahuje priemernú zložitosť . V prípade hľadania krátkych slov, ktorých je v texte veľa alebo použitia nevhodnej hashovacej funkcie je zložitosť podobná ako pri použití naivného vyhľadávania.
==== Algoritmus založený na konečných automatoch ====
Pre danú vzorku //P[1..m]// skonštruujeme konečný automat . Následne text spracujeme automatom //A//.
FINITE_AUTOMATON_MATCHER(T,A)
q ← 0
for i = 1 to n do
q ← δ(q,T[i])
if q = m then print i-m je platný posun fi
od
Zložitosť spracovania textu je , musíme ale ešte skonštruovať konečný automat //A// (jeho prechodovú funkciu).
Nech a . Definujeme sufixovú funkciu , kde je dĺžka najdlhšieho prefixu vzorky //P//, ktorý je sufixom slova //x//.
Napr. pre //P = popapopa// je , , , .
Konečný automat pre //P// je , kde prechodová funkcia je definovaná predpisom:
Prechodovú funkciu konštruujeme podľa nasledovného algoritmu:
1 AUTOMAT(P,Σ)
2 for q = 0 to m do
3 for každé a ∈ Σ do
4 k ← min(m+1,q+2)
5 repeat k ← k-1 until P_k je sufixom P_qa
6 δ(q,a) ← k
7 od
8 od
9 return δ
Zložitosť konštrukcie automatu je (riadok 2 **m**-krát, riadok 3 **|Σ|**-krát a riadok 5 v najhoršom prípade **m2**-krát). Existuje efektívnejšia procedúra zložitosti .
**Zložitosť predspracovania** je teda v a **zložitosť vyhľadávania** pomocou konečných automatov v .
Príkladom automatu, ktorý v texte rozoznáva reťazce //abaa// je konečný automat v otázke [[home:inf:ap9#prechodovym_grafem|Konečné automaty]], ktorý si jemne upravíme:
===== Predmety =====
* [[https://is.muni.cz/auth/predmety/predmet.pl?fakulta=1433;obdobi=3725;id=427609;|IB108]]: Návrh algoritmů II, prof. RNDr. Ivana Černá, CSc.
* [[https://is.muni.cz/auth/predmety/predmet.pl?fakulta=1433;obdobi=3725;id=427675;|PV030]]: Textové informační systémy, RNDr. Petr Sojka, Ph.D.
===== Literatúra =====
* [[http://en.wikipedia.org/wiki/Karp-Rabin|Rabin-Karp string search algorithm]], Wikipedia
* [[http://www.fi.muni.cz/usr/cerna/NAII/|slidy k Návrhu algoritmov II]]
===== Vypracoval =====
Dušan Katona, ICQ: 426 081 873
hotovo: <99%>
môžete kluďne niečo doplniť alebo opraviť
Otázku si přečetl pan RNDr. Jan Bouda a rámcově prošel. Jeho podněty pro doplnění textu, opravy nesrovnalostí a odstranění matoucích či k otázce se nevztahujících textů byly do otázky zaneseny. Tato kontrola je jen **rámcová**, stále se může stát, že v otázce zůstala zapomenutá chybka či nesrovnalost, vyučující za toto nenese odpovědnost, berte tuto rámcovou kontrolu jako formu pomoci od vyučujících pro studenty.
~~DISCUSSION~~