From 9313f25559824002b787897ee3f09a70082730e6 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Tue, 10 Jan 2012 22:38:39 +0100 Subject: [PATCH] KMP: Zaklad algoritmu AC, zatim bez konstrukce automatu --- 1-kmp/1-kmp.tex | 176 ++++++++++++++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 64 deletions(-) diff --git a/1-kmp/1-kmp.tex b/1-kmp/1-kmp.tex index dc111bc..87c5c93 100644 --- a/1-kmp/1-kmp.tex +++ b/1-kmp/1-kmp.tex @@ -135,7 +135,7 @@ z~p \:Vrátíme nový stav~$I$. \endalgo -\s{Hledej($\sigma$):} +\s{Hledej($\sigma$):} \cmt{Spu¹tìní automatu na øetìzec~$\sigma$.} \algo \:$I \leftarrow 0$. \:Pro znaky $x\in\sigma$ postupnì provádíme: @@ -231,78 +231,128 @@ Line s~délkou sena pak potøebujeme na~samotné vyhledání. \qed -\h{Hledání více øetìzcù najednou} -Nyní si zahrajeme tuté¾ hru, ov¹em v~trochu slo¾itìj¹ích kulisách. Podíváme se na~algoritmus, který si poradí i~s více ne¾ jednou jehlou. -Mìjme tedy jehly $\iota_1 \dots \iota_n$, a~jejich délky $J_i = \vert \iota_i \vert $. Dále budeme potøebovat seno $\sigma$ délky $S=\vert \sigma \vert$. - -Pøedtím, ne¾ se pustíme do~vlastního vyhledávacího algoritmu, mo¾ná bychom si mìli ujasnit, co vlastnì bude jeho výstupem. U problému hledání jedné jehly to bylo jasné -- byla to nìjaká mno¾ina pozic v~senì, na~kterých zaèínaly výskyty jehly. Jak tomu ale bude zde? Sice bychom také mohli vrátit pouze mno¾inu pozic, ale my budeme chtít malièko víc. Budeme toti¾ chtít vìdìt i~to, která jehla se na~které pozici vyskytuje. Výstup tedy bude vypadat následovnì: $V = \{(i,j)~\vert~\sigma[i:i+J_j]= \iota_j \}$. - -Zde se v¹ak skrývá jedna drobná zrada. Budeme se asi muset vzdát nadìje, ¾e najdeme algoritmus, jeho¾ slo¾itost je lineární v~celkové délce v¹ech jehel a~sena. Výstup toti¾ mù¾e být del¹í ne¾ lineární. Mù¾e se nám klidnì stát, ¾e na~jedné pozici v~senì se bude vyskytovat více rùzných jehel -- pokud bude jedna jehla prefixem jiné (co¾ jsme nikde nezakázali), tak máme povinnost ohlásit oba výskyty. Vzhledem k~tomu budeme hledat takový algoritmus, který bude lineární v~délce vstupu plus délce výstupu, co¾ je evidentnì to nejlep¹í, èeho mù¾eme dosáhnout. - -Algoritmus, který si nyní uká¾eme, vymysleli nìkdy v~roce 1975 pan Aho a~paní Corasicková. Bude to takové zobecnìní Knuthova-Morrisova-Prattova algoritmu. - -\h{Algoritmus Aho-Corasicková} - -Opìt se budeme sna¾it sestrojit nìjaký vyhledávací automat a~nìjakým zpùsobem tento automat pou¾ít k~procházení sena. Podívejme se nejprve na~pøíklad. Budeme chtít vyhledávat tato slova: |ara|, |bar|, |arab|, |baraba|, |barbara|. Mìjme tedy tìchto pìt jehel a~rozmysleme si, jak by vypadal nìjaký automat, který by tato slova umìl zatím jenom rozpoznávat. Pro jedno slovo automat vypadal jako cesta, zde u¾ to bude strom. (viz obrázek). - -\figure{ara_strom_blank.eps}{Vyhledávací automat -- strom.}{1in} - -Navíc budeme muset do~automatu zanést, kde nìjaké slovo konèí. V~pùvodním automatu pro jedno slovo to bylo jednoduché -- ono jedno jediné slovo odpovídalo poslednímu vrcholu cesty. Tady se v¹ak slova mohou vyskytovat vícekrát a~konèit nejenom v~listech ale i~v~nìjakém vnitøním vrcholu (co¾ se stane tehdy, pokud je jedno hledané slovo prefixem jiného hledaného slova). Formálnì to nebudeme dokazovat, ale snadno nahlédneme, ¾e listy stromu odpovídají hledaným slovùm, ale opaènì to neplatí. - -\figure{ara_strom_end.eps}{Vyhledávací automat s~konci slov.}{1in} - -Dále bychom mìli do~automatu pøidat zpìtné hrany. Jejich definice bude úplnì stejná jako u automatu pro hledání jednoho slova. Jinými slovy z~ka¾dého stavu pùjde zpìtná hrana do~nejdel¹ího vlastního suffixu, který je stavem. Èili kdy¾ budeme mít nìjaké jméno stavu, budeme se ho sna¾it co nejménì (ale alespoò o~znak) zkrátit zleva, abychom zase dostali jméno stavu. Z~koøene -- prázdného stavu -- pak evidentnì ¾ádná zpìtná hrana nepovede. - -\figure{ara_strom_final.eps}{Vyhledávací automat se zpìtnými hranami.}{1,25in} - -Zbývá nám je¹tì si rozmyslet, jakým zpùsobem bude ná¹ automat hlásit výstup. Opìt smìøujeme k~tomu, aby se automat po~pøeètení nìjakého kusu textu nacházel ve~stavu odpovídajícímu nejdel¹ímu mo¾nému suffixu toho textu. Zatímco u hledání jediné jehly bylo hlá¹ení výskytù jednoduché -- kdykoliv jsme se dostali na~konec \uv{automatové cestièky} tady to bude opìt slo¾itìj¹í. - -První, co se nabízí, je vyu¾ít toho, ¾e jsme si oznaèili nìjaké vrcholy, kde hledaná slova konèí. Co tedy zkusit hlásit výskyt tohoto slova v¾dy, kdy¾ pøijdeme do~nìjakého oznaèeného vrcholu? Tento zpùsob v¹ak nefunguje, pokud se uvnitø nìkteré jehly skrývá jehla vnoøená. Napøíklad po~pøeètení slova |bara|, nám ná¹ souèasný automat neøíká, ¾e bychom mìli nìjaké slovo ohlásit, a~pøitom tam evidentì konèí podøetìzec |ara|. Stejnì tak pokud pøeèteme |barbara|, u¾ si nev¹imneme toho, ¾e tam konèí zároveò i~|ara|. Pouhé \uv{hlá¹ení teèek} tedy nefunguje. - -Dále si mù¾eme v¹imnout toho, ¾e v¹echna slova, která by se mìla v~daném stavu hlásit, jsou suffixy jména tohoto stavu. Pøitom víme, ¾e zpìtná hrana jméno stavu zkracuje zleva. Tak¾e speciálnì v¹echny suffixy daného stavu, které jsou také stavy, se dají najít tak, ¾e se vydáme po~zpìtných hranách do~koøene. Nabízí se tedy v¾dy projít cestu po~zpìtných hranách a¾ do~koøene a~hlásit v¹echny \uv{teèky}. Tento zpùsob by nám v¹ak celý algoritmus znaènì zpomalil, proto¾e cesta do~koøene mù¾e být relativnì dlouhá, ale teèek na~ní obvykle bude málo. - -Mohli bychom také zkusit si pro ka¾dý stav $\beta$ pøedpoèítat mno¾inu $cache(\beta)$, která by obsahovala v¹echna slova, která máme hlásit, kdy¾ se ve~stavu $\beta$ nacházíme. Pokud pak do~tohoto stavu vstoupíme, podíváme se na~tuto mno¾inu a~vypí¹eme v¹e, co v~ní je. Výpis nám bude evidentnì trvat lineárnì k~velikosti mno¾iny, celkovì tedy lineárnì k~velikosti výstupu. Problém je ale ten, ¾e jednotlivé cache mohou být hodnì velké, tak¾e je nestihneme sestrojit v lineárním èase. (Rozmyslete si pøíklad slovníku, kdy se to stane.) - -To, co nám ale ji¾ opravdu pomù¾e, bude zavedení zkratek. V¹imli jsme si, ¾e po~zpìtných hranách mù¾eme projít do~koøene a~hlásit v¹echny nalezené teèky. Vadilo nám ale, ¾e se mù¾e stát, ¾e budeme dlouho po~cestì chodit a~pøi tom ¾ádné teèky nenalézat. Zavedeme si proto zkratky k~nejbli¾¹í teèce. - -\s{Definice} (zkratková hrana): -Budeme mít tedy nìjakou funkci $slovo(\beta) :=$ slovo, které konèí ve~stavu $\beta$ (nebo $\emptyset$, pokud ¾ádné takové slovo není). Dále pak funkci $out(\beta) :=$ nejbli¾¹í vrchol dosa¾itelný po~zpìtných hranách, èili nejdel¹í vlastní suffix stavu $\beta$, v~nìm¾ je definovaná funkce $slovo$. Trochu lid¹tìji øeèeno, ten nejbli¾¹í dosa¾itelný vrchol, ve~kterém je teèka. - -Po pøidání tìchto zkratkových hran ji¾ máme reprezentaci, ve~které opravdu umíme v~daném stavu vyjmenovat v¹echna slova, která máme vypsat, a~to v~èase lineárním s~tím, kolik tìch slov je. +\h{Hledání více øetìzcù najednou: algoritmus Aho-Corasicková} + +Nyní si zahrajeme tuté¾ hru, ov¹em v~trochu slo¾itìj¹ích kulisách. Tentokrát +bude jehel vícero: $\iota_1, \ldots, \iota_N$, jejich délky oznaèíme $J_I = \vert \iota_I \vert $. +Opìt dostaneme nìjaké seno~$\sigma$ délky $S=\vert \sigma \vert$ a chceme nalézt +v¹echny výskyty jehel v~senì. + +Pøedtím, ne¾ se pustíme do~vlastního vyhledávacího algoritmu, mìli bychom si +ujasnit, co bude jeho výstupem. Dokud byla jehla jedna jediná, bylo to jasné -- +chtìli jsme nalézt mno¾inu v¹ech pozic v~senì, na~kterých zaèínaly výskyty +jehly. Jak tomu bude zde? Budeme chtít vìdìt, která jehla se vyskytuje na které +pozici. Jinými slovy budeme chtít vypsat v¹echny dvojice $(K,I)$ takové, +¾e $\sigma[K:K+J_I]= \iota_I$. + +Zde se v¹ak skrývá jedna drobná zrada. Budeme se asi muset vzdát nadìje +na algoritmus, jeho¾ slo¾itost je lineární v~celkové délce v¹ech jehel +a~sena. Výstup toti¾ mù¾e být del¹í ne¾ lineární. Pokud je toti¾ jedna +jehla suffixem druhé, na jedné pozici v~senì mohou konèit výskyty obou. +Proto budeme hledat algoritmus, který bude lineární v~délce vstupu plus +délce výstupu, co¾ je evidentnì to nejlep¹í, èeho mù¾eme dosáhnout. + +Algoritmus, který si nyní uká¾eme, objevili v~roce 1975 pan Aho a~paní +Corasicková. Je elegantním zobecnìním Knuthova-Morrisova-Prattova algoritmu pro +více øetìzcù. + +\s{Vyhledávací automat:} +Opìt se budeme sna¾it sestrojit vyhledávací automat, jeho¾ stavy budou +odpovídat prefixùm jehel a dopøedné hrany roz¹iøování prefixù o~jeden znak. +Tentokrát navíc nebude jasnì definovaný koncový stav, oznaèíme si proto +v¹echny stavy, které odpovídají nìkteré z~jehel (na pøíkladu na obrázku +je vidìt, ¾e to nemusí být jen listy). + +\figure{ara_strom_zkr.eps}{Vyhledávací automat pro slova |ara|, |bar|, |arab|, |baraba| a |barbara|.}{1.25in} + +Dále potøebujeme zpìtné hrany (na obrázku èerné ¹ipky). +Jejich definice bude úplnì stejná jako u~automatu KMP. +Z~ka¾dého stavu pùjde zpìtná hrana do~jeho nejdel¹ího vlastního suffixu, který je také +stavem. Èili se budeme sna¾it jméno stavu zkracovat zleva tak dlouho, ne¾ dostaneme +opìt jméno stavu. Z~koøene -- prázdného stavu -- pak evidentnì ¾ádná zpìtná hrana nepovede. + +Funkce pro hledání v~senì bude vypadat stejnì jako u~KMP: zaène v~poèáteèním +stavu (to je koøen stromu), postupnì bude roz¹iøovat seno o~dal¹í písmenka, +poka¾dé zkusí jít dopøednou hranou a pokud to nejde, bude se vracet po~zpìtných +hranách tak dlouho, a¾ buïto bude existovat vhodná dopøedná hrana, nebo se vrátí +do koøene stromu a tehdy nový znak zahodí. + +Stejnì jako u~KMP nahlédneme, ¾e procházení sena trvá $\O(S)$ a ¾e platí analogický +invariant, toti¾ ¾e se v~ka¾dém okam¾iku nacházíme ve~stavu, který odpovídá nejdel¹ímu +suffixu zatím pøeèteného sena, který je prefixem nìkteré jehly. + +\s{Hlá¹ení výskytù:} +Jediné, co se bude od KMP li¹it, je, kdy ohlásit výskyt. U~KMP to bylo snadné: kdykoliv +jsme dospìli do posledního stavu, znamenalo to nalezení jehly. Nabízí se hlásit +výskyt, kdykoliv dojdeme do stavu oznaèeného jako koncový. To ale nefunguje: +pokud ná¹ ukázkový automat pøeète seno |bara|, skonèí ve stavu~|bara|, a~pøitom +by mìl ohlásit výskyt jehly |ara|. Stejnì tak pøeèteme-li |barbara|, nev¹imneme +si, ¾e na tém¾e místì konèí i |ara|. + +Platí ale, ¾e v¹echna slova, která bychom mìli v~daném stavu ohlásit, jsou suffixy +jména tohoto stavu. Mohli bychom je tedy najít tak, ¾e se vydáme po zpìtných hranách +a¾ do koøene a kdykoliv projdeme pøes koncový vrchol, ohlásíme výskyt. To ov¹em +trvá pøíli¹ dlouho -- jistì by se stávalo, ¾e bychom podnikli dlouhou cestu do koøene +a nena¹li na ní ani jeden výskyt. + +Dal¹í, co se nabízí, je pøedpoèítat si pro ka¾dý stav~$\beta$ mno¾inu slov ${\cal S}(\beta)$, +jejich¾ výskyty máme v~tomto stavu hlásit. To by fungovalo, ale existují mno¾iny jehel, +pro které bude celková velikost mno¾in ${\cal S}(\beta)$ superlineární. Museli bychom se +tedy vzdát lákavé mo¾nosti stavby automatu v~lineárním èase. (Rozmyslete si, jak by +takové jehly vypadaly.) + +Jak to tedy vyøe¹íme? Zavedeme zkratky (na obrázku zelenì): \s{Definice:} -Vyhledávací automat sestává ze stromu dopøedných hran (vrcholy jsou prefixy jehel, hrany odpovídají roz¹íøení o~písmenko), zpìtných hran ($z(\beta) :=$ nejdel¹í vlastní suffix slova $\beta$, který je stavem) a~zkratkových hran. - -Automat pak bude na~na¹em pøíkladu vypadat takto (zkratkové hrany jsou znázornìny zelenì): - -\figure{ara_strom_zkr.eps}{Vyhledávací automat se zkratkovými hranami.}{1,25in} - -Nyní u¾ nám zbývá jenom vlastní algoritmus -- nejdøív popí¹eme algoritmus, který bude hledat pomocí takového automatu, a~potom se pustíme do~toho, jak se takový automat staví. +{\I Zkratková hrana} ze~stavu~$\alpha$ vede do nejbli¾¹ího stavu $\zeta(\alpha)$ dosa¾itelného +z~$\alpha$ po zpìtných hranách, který je koncový. + +Jinými slovy, $\zeta(\alpha)$ nám øekne, jaký je nejdel¹í vlastní suffix slova~$\alpha$, který +je jehlou. Pokud takový suffix neexistuje, ¾ádná zkratková hrana ze~stavu~$\alpha$ nepovede. +Pomocí zkratkových hran mù¾eme snadno vyjmenovat v¹echny výskyty. Budeme postupovat stejnì, +jako bychom procházeli po v¹ech zpìtných hranách, jen budeme dlouhé úseky zpìtných hran, na~nich¾ +není nic k~hlá¹ení, pøeskakovat v~konstantním èase. + +\s{Reprezentace automatu:} +Vyhledávací automat se tedy sestává ze stromu dopøedných hran, ze zpìtných +hran a ze~zkratkových hran. Ne¾ vyslovíme samotný algoritmus AC, rozmysleme si, jak automat +ulo¾it do pamìti. Pro ka¾dý stav si budeme pamatovat: +\itemize\ibull +\:$I$ -- poøadové èíslo stavu (tøeba v~poøadí, jak vrcholy vznikaly), +\:$\(I)$ -- kam z~nìj vede zpìtná hrana (vyu¾íváme toho, ze mù¾e být nejvý¹e jedna, tak¾e si zapamatujeme + èíslo stavu, do nìj¾ vede), +\:$\(I)$ -- kam z~nìj vede zkratková hrana (takté¾), +\:$\(I)$ -- zda tu konèí nìjaké slovo (a~pokud ano, tak které), +\:$\(I,x)$ -- kam vede dopøedná hrana oznaèená písmenem~$x$ (pro malé abecedy si to + mù¾eme pamatovat v~poli, pro velké tøeba v~he¹ovací tabulce nebo stromu). +\endlist -Nejprve si nadefinujeme, jak vypadá jeden krok automatu. Bude to vlastnì nìjaká funkce, která dostane stav a~písmenko. Ona nás pak pomocí tohoto písmenka posune po~automatu. ($f(\alpha, x)$ bude dopøedná hrana ze stavu $\alpha$ oznaèená písmenem~$x$) +\>Celý algoritmus pro zpracování sena automatem pak bude vypadat takto: -\s{Krok ($\alpha$, $x$):} +\s{Krok($I$, $x$):} \cmt{Jeden krok automatu: jsme ve stavu~$I$, pøeèetli jsme znak~$x$.} \algo -\:Dokud $f(\alpha, x) = \emptyset~\&~\alpha \neq \~~\alpha \leftarrow z(\alpha)$. -\:Pokud $f(\alpha, x) \neq \emptyset:~~\alpha \leftarrow f(\alpha, x)$. -\:Vrátíme výsledek. +\:Dokud $\(I, x) = \emptyset~\&~I \ne \$: $I \leftarrow \(I)$. +\:Pokud $\(I, x) \ne \emptyset$: $I \leftarrow \(I,x)$. +\:Vrátíme nový stav~$I$. \endalgo -\s{Hledání:} +\s{Hledej($\sigma$):} \cmt{Spu¹tìní automatu na øetìzec~$\sigma$.} \algo -\:$\alpha \leftarrow \$. -\:Pro znaky $x$ ze slova $\sigma$: -\::$\alpha \leftarrow \(\alpha, x)$. -\::$\beta \leftarrow \alpha$ -\::Dokud $\beta \neq \emptyset$: -\:::Je-li $\(\beta) \neq \emptyset$: -\::::Ohlásíme $\(\beta)$. -\:::$\beta \leftarrow \(\beta)$. +\:$I \leftarrow \$. +\:Pro znaky $x\in\sigma$ postupnì provádíme: +\::$I \leftarrow \(I, x)$. +\::$K \leftarrow I$. +\::Dokud $K \neq \emptyset$: +\:::Je-li $\(K) \neq \emptyset$: +\::::Ohlásíme $\(K)$. +\:::$K \leftarrow \(K)$. \endalgo -Algoritmus hledání vlastnì není nic jiného, ne¾ prosté projití po~zelených zkratkových hranách ze stavu $\alpha$, ve~kterém právì jsme, a~ohlá¹ení v¹eho, co po~cestì najdeme. +\>Jak u¾ jsme nahlédli, v¹echny kroky automatu dohromady trvají $\O(S)$. +Mimo to je¹tì hlásíme výskyty, co¾ trvá $\O(\)$. Zbývá +ukázat, jak automat sestrojit. -V ka¾dém okam¾iku se automat nachází ve~stavu, který odpovídá nejdel¹ímu mo¾nému suffixu toho, co jsme u¾ pøeèetli. Dùkaz tohoto invariantu je stejný jako u verze automatu pro hledání pouze jedné jehly, nebo» vychází pouze z~definice zpìtných hran. Podobnì nahlédneme, ¾e èasová slo¾itost vyhledávací procedury je lineární v~délce sena plus to, co spotøebujeme na~hlá¹ení výskytù. Nejprve na~chvíli zapomeneme, ¾e nìjaké výskyty hlásíme a~spoèítáme jenom kroky. Ty mohou vést dopøedu a~zpátky. Krok dopøedu prodlu¾uje jméno stavu o~jedna, krok dozadu zkracuje aspoò o~jedna. Tudí¾ krokù dozadu je maximálnì tolik, co krokù dopøedu a~krokù dopøedu je maximálnì tolik, kolik je délka sena. V¹echny kroky dohromady tedy trvají $\O(S)$. Hlá¹ení výskytù pak trvá $\O(S~+ \vert V \vert)$. Celé hledání tedy trvá lineárnì v~délce vstupu a~výstupu. +\h{XXX --- Pod tímto místem nepøepsáno --- XXX} Zbývá nám u¾ jen konstrukce automatu. Opìt vyu¾ijeme faktu, ¾e zpìtná hrana ze stavu $\beta$ vede tam, kam by se dostal automat pøi hledání $\beta$ bez prvního písmenka. Tak¾e zase chceme nìco, jako simulovat výpoèet toho automatu na~slovech bez prvního písmenka a~doufat v~to, ¾e si vystaèíme s~tou èástí automatu, kterou jsme u¾ postavili. Tentokrát to v¹ak nemù¾eme dìlat jedno slovo po~druhém, proto¾e zpìtné hrany mohou vést køí¾em mezi jednotlivými vìtvemi automatu. Mohlo by se nám tedy stát, ¾e pøi hledání nìjakého slova potøebujeme zpìtnou hranu, která vede do~jiného slova, které jsme je¹tì nezkonstruovali. Tak¾e tento postup sel¾e. Mù¾eme v¹ak vyu¾ít toho, ¾e ka¾dá zpìtná hrana vede ve~stromu alespoò o~jednu hladinu vý¹. Mù¾eme tak strom konstruovat po~hladinách. Lze si to tedy pøedstavit tak, ¾e paralelnì spustíme vyhledávání v¹ech slov bez prvních písmenek a~v¾dycky udìláme jeden podkrok ka¾dého z~tìch hledání, co¾ nám dá zpìtné hrany z~dal¹ího patra stromu. @@ -328,8 +378,6 @@ To, \s{Vìta:} Algoritmus Aho-Corasicková najde v¹echny výskyty v~èase $$\O\left(\sum_i~\iota_i~+~S~+~\sharp\\right).$$ -Je¹tì se na~závìr zamysleme, jak bychom si takový automat ukládali do~pamìti. Urèitì se nám bude hodit si stavy nìjak oèíslovat (tøeba v~poøadí, v~jakém budou vznikat). Potom funkce pro zpìtné a~zkratkové hrany mohou být reprezentované polem indexovaným èíslem stavu. Funkce {\I Slovo}, která øíká, jaké slovo ve~stavu konèí, zase mù¾e být pole indexované stavem, které nám øekne poøadové èíslo slova ve~slovníku. Pro dopøedné hrany v~ka¾dém vrcholu pak mù¾eme mít pole indexované písmenky abecedy, které nám pro ka¾dé písmenko øekne, buï ¾e taková hrana není, nebo nám øekne, kam tato hrana vede. Je vidìt, ¾e takovéto pole se hodí pro pomìrnì malé abecedy. U¾ pro abecedu A-Z~bude velikosti 26 a~z~vìt¹iny bude prázdné, tak¾e bychom plýtvali pamìtí. V praxi se proto èasto pou¾ívá hashovací tabulka. Pøípadnì bychom mohli mít i~jen jednu velkou spoleènou hashovací tabulku, která bude reprezentovat funkci celou, ve~které budou zahashované dvojice (stav, písmenko). Tìchto dvojic je evidentnì tolik, kolik hran stromu, èili lineárnì s~velikostí slovníku, a~je to asi nejkompaktnìj¹í reprezentace. - \h{Rabinùv-Karpùv algoritmus} Nyní si uká¾eme je¹tì jeden algoritmus na~hledání jedné jehly, který nebude mít v~nejhor¹ím pøípadì lineární slo¾itost, ale bude ji mít prùmìrnì. Bude daleko jednodu¹¹í a~uká¾e se, ¾e je v~praxi daleko rychlej¹í. Bude to algoritmus zalo¾ený na~hashování. -- 2.39.2