X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=6-kmp%2F6-kmp.tex;h=53041769c1b74ebe52d2322e3df89502d1903828;hb=5fcfe1a176352dc85a4bc25e0321b1d900b07717;hp=a0adcc58ada9071fc9b77b30bf3dd31a48991376;hpb=92283d76b2c15f67b318e8aa3cfac7ff2b8380fc;p=ads2.git diff --git a/6-kmp/6-kmp.tex b/6-kmp/6-kmp.tex index a0adcc5..5304176 100644 --- a/6-kmp/6-kmp.tex +++ b/6-kmp/6-kmp.tex @@ -2,123 +2,126 @@ \prednaska{6}{Vyhledávání v textu}{(zapsal K. Ka¹èák, M. Klauèo, M. Vachna)} -\s{Úkol:} V textu najít v¹echny výskyty hledaného slova(hledaných slov). +\s{Úkol:} V~textu s~délkou $S$ najít v¹echny výskyty hledaného slova s~délkou $J$ (hledaných slov). -\h{Hloupý algoritmus} +\h{Hloupý algoritmus} -Algoritmus prochází sekvenènì textem a hledaným vzorovým slovem. Pøi neshodì se ve vzorovem slovì vrací na zaèátek a v textu pokraèuje znakem, v kterém nastala neshoda. Èasová slo¾itost je $\O(S)$, kde $S$ je délka textu. Tento algoritmus funguje pouze jen pro vzorové slová bez opakujících se znakù. +Algoritmus prochází sekvenènì textem a hledaným vzorovým slovem. Pøi neshodì se ve vzorovem slovì vrací na zaèátek a v~textu pokraèuje znakem, v~kterém nastala neshoda. Èasová slo¾itost je $\O(S)$. Tento algoritmus funguje pouze jen pro vzorová slova bez opakujících se znakù. -\s{Pøíklad:} Hledání vzorového slova $JEHLA$ v textu $VKUPCEJEJEHLA$. Ve chvíli kdy máme prefix $JE$ a na vstupu dostaneme $J$, dochází k neshodì a pokraèujeme v hledání od tohoto znaku. +\s{Pøíklad:} Hledání vzorového slova |jehla| v~textu |vkupcejejehla|. Ve chvíli kdy máme prefix |je| a na vstupu dostaneme |j|, dochází k~neshodì a pokraèujeme v~hledání od tohoto znaku. \h{Neefektivní algoritmus} -Algoritmus prochází text od zaèátku a¾ do konce a pro ka¾dou pozici v textu zkontroluje, zda na této pozici nezaèíná hledané slovo. Tak pro ka¾dou pozici provede a¾ $S$ porovnání znakù, èili celkem a¾ $SJ$ porovnání. Proto je èasová slo¾itost $\O(SJ)$, kde $S$ je délka textu a $J$ délka vzorového slova. +Algoritmus prochází text od zaèátku a¾ do konce a pro ka¾dou pozici v~textu zkontroluje, zda na této pozici nezaèíná hledané slovo. Tak pro ka¾dou pozici provede a¾ $S$ porovnání znakù, èili celkem a¾ $SJ$ porovnání. Proto je èasová slo¾itost $\O(SJ)$. \h{Chytrý algoritmus} -Algoritmus je vylep¹ením Neefektivního algoritmu, konkretnì zpùsobu jakým sa vrací v textu pøi neshodì mezi znakem textu a -znakem vzorového slova. +Algoritmus je vylep¹ením Neefektivního algoritmu, konkretnì zpùsobu jakým sa vrací v textu pøi neshodì mezi znakem textu a +znakem vzorového slova. -\s{Pøíklad:} Pro vzorové slovo $AJAAJAK$ jsme na¹li v textu prefix $AJAAJA$. Oèakávame $K$. +\s{Pøíklad:} Pro vzorové slovo |ajaajak| jsme na¹li v~textu prefix |ajaaja|. Oèakávame |k|. \itemize\ibull -\:Kdy¾ ale dostaneme $A$ a budeme mít prefix $AJAAJAA$, vracíme se v textu za první $AJA$, tedy prefix zkrátíme na $AJAA$ a pokraèujeme v hledání. -\:Kdy¾ je nasledující znak $J$ a budeme mít prefix $AJAAJAJ$, vracíme se v textu za $AJAAJ$, tedy prefix zkrátíme na $AJ$ a pokraèujeme v hledání. -\:V pøípadì, ¾e dostaneme jiný znak, v textu se nevracíme a pokraèujeme dal¹ím znakem v textu. +\:Kdy¾ ale dostaneme |a| a budeme mít prefix |ajaajaa|, vracíme se v~textu za první |aja|, tedy prefix zkrátíme na |ajaa| a pokraèujeme v~hledání. +\:Kdy¾ je nasledující znak |j| a budeme mít prefix |ajaajaj|, vracíme se v~textu za |ajaaj|, tedy prefix zkrátíme na |aj| a pokraèujeme v~hledání. +\:V~pøípadì, ¾e dostaneme jiný znak, v~textu se nevracíme a pokraèujeme dal¹ím znakem v~textu. \endlist - -\s{Definice a znaèení pro øetìzce(slová):} + +\s{Definice a znaèení pro øetìzce (slova):} \itemize\ibull \s{Definice:} \itemize\ibull -\:Abeceda $\sum$ je koneèná mno¾ina znakù, s kterých tvoøíme text, øetìzece, slová jako koneèné posloupnosti znakù z $\sum$. Pøíkladem extrémních abeced je lineární abeceda slo¾ená s nul a jednièek. Pøíklad s druhého konce je abecade, která má jako znaky slova èeského jazyka. V algoritmech nebudeme uva¾ovat velikost abecedy (poèet znakù). -\:$\sum$* je mno¾ina v¹ech slov nad abecedou $\sum$. +\:{\I Abeceda $\Sigma$} je koneèná mno¾ina znakù, ze kterých tvoøíme text, øetìzece, slova jako koneèné posloupnosti znakù ze $\Sigma$. Pøíkladem extrémních abeced je lineární abeceda slo¾ená z~nul a jednièek. Pøíklad s~druhého konce je abeceda, která má jako znaky slova èeského jazyka. V algoritmech nebudeme uva¾ovat velikost abecedy (poèet znakù). +\:{\I $\Sigma$*} je mno¾ina v¹ech slov nad abecedou $\Sigma$. \endlist \s{Znaèení:} \itemize\ibull -\:Slová budeme znaèit malými písmenami øecké abecedy $\alpha$,$\beta$... a znaky velkými písmenami latinky $A$,$B$... . -\:Prázdné slovo znaèíme písmenem $\epsilon$. -\:Délka slova $\vert \alpha \vert$ pro $\alpha \in \sum*$ je poèet znakù. -\:Zøetìzení $\alpha\beta$ vznikne zapsáním slov $\alpha$ a $\beta$ za sebe. Platí $\alpha\epsilon=\epsilon\alpha=\alpha$, $\vert \alpha\beta \vert=\vert \alpha \vert+\vert \beta \vert$. +\:{\I Slova} budeme znaèit malými písmeny øecké abecedy $\alpha$, $\beta$, \dots, +\:{\I Znaky} velkými písmeny latinky $A$, $B$, \dots +\:{\I Prázdné slovo} znaèíme písmenem $\varepsilon$. +\:{\I Délka slova} $\vert \alpha \vert$ pro $\alpha \in \Sigma^*$ je poèet znakù. +\:{\I Zøetìzení} $\alpha\beta$ vznikne zapsáním slov $\alpha$ a $\beta$ za sebe. Platí: $\alpha\varepsilon=\varepsilon\alpha=\alpha$, $\vert \alpha\beta \vert=\vert \alpha \vert+\vert \beta \vert$. \:$\alpha[i]$ je $i$-té písmeno slova $\alpha$, indexuje se od $0$. -\:$\alpha[i:j]$ je podslovo tvoøené písmenami $\alpha[i]$,...,$\alpha[j-1]$. Pøíklady: $\alpha[i:i+1]=\alpha[i]$, $\alpha[i:i]=\epsilon$, $\alpha[:]=\alpha$. +\:$\alpha[i:j]$ je podslovo tvoøené písmeny $\alpha[i]$,...,$\alpha[j-1]$. Pøíklady: $\alpha[i:i+1]=\alpha[i]$, $\alpha[i:i]=\varepsilon$, $\alpha[:]=\alpha$. \:$\alpha[:j]$ je prefix obsahující prvních $j$ znakù slova $\alpha$. \:$\alpha[i:]$ je suffix obsahující znaky slova $\alpha$ poèínaje $i$-tým znakem. -\:Ka¾dé slovo je prefixem i suffixem sebe sama, takovému prefixu/suffixu øíkáme vlastní. V¹em ostatním nevlastní. +\:Ka¾dé slovo je prefixem i suffixem sebe sama, takovému prefixu resp. suffixu øíkáme {\I nevlastní}. V¹em ostatním {\I vlastní}. \:Prázdné slovo je podslovem, prefixem i suffixem ka¾dého slova vèetnì prázdného slova. \endlist \endlist -\s{Problém:} +\s{Problém:} \itemize\ibull \s{IN:} \itemize\ibull -\:$\iota$ slovo (jehla) délky $J=\vert \iota \vert$ -\:$\sigma$ text (seno) délky $S=\vert \sigma \vert$ +\:$\iota$ slovo (jehla) délky $J=\vert \iota \vert$, +\:$\sigma$ text (seno) délky $S=\vert \sigma \vert$. \endlist \s{OUT:} \itemize\ibull -\:$\left\{ i\vert \sigma[i:i+J]=\iota \right\}$ +\:V¹echny výskyty slova $\iota$ v textu $\sigma$: $\left\{ i : \sigma[i:i+J]=\iota \right\}.$ \endlist \endlist \h{Vyhledávací automat (Knuth, Morris, Pratt)} -Vyhledávací automat bude vlastnì graf jeho¾ vrcholy reprezentují stavy. Jména stavù budou v¹echny prefixy slova $\iota$. Poèáteèný stav je prázdny slovo $\epsilon$ a koneèný je samotná $\iota$. Dopøední hrany grafu budú popisovat pøechod mezi stavy v smysle zvìt¹ení délky jména stavu (dopøedná funkce $d(\alpha , X)$), tedy ka¾dá taková hrana bude oznaèena písmenem $X$ a bude popisovat dané zvìt¹ení délky jména stavu, tedy $\alpha \rightarrow \alpha X$. Zpìtné hrany grafu budú popisovat pøechod ( zpìtná funkce $z(\alpha)$) mezi stavem $\alpha$ a nejdel¹ím vlastním suffixem $\alpha$, který je prefixem $\iota$, kdy¾ nastane neshoda. +Vyhledávací automat bude vlastnì graf, jeho¾ vrcholy reprezentují stavy. Jmény stavù budou v¹echny prefixy slova $\iota$. Poèáteèní stav je prázdné slovo $\varepsilon$ a koneèný je samotná $\iota$. Dopøedné hrany grafu budou popisovat pøechod mezi stavy ve smyslu zvìt¹ení délky jména stavu (dopøedná funkce $d(\alpha , X)$), tedy ka¾dá taková hrana bude oznaèena písmenem $X$ a bude popisovat dané zvìt¹ení délky jména stavu, tedy $\alpha \rightarrow \alpha X$. Zpìtné hrany grafu budou popisovat pøechod (zpìtná funkce $z(\alpha)$) mezi stavem $\alpha$ a nejdel¹ím vlastním suffixem $\alpha$, který je prefixem $\iota$, kdy¾ nastane neshoda. \figure{vautomat.eps}{Vyhledávací automat}{5.5in} \s{Vyhledávaní:} \algo -\:$\alpha \leftarrow \epsilon$. -\:pro $C\in\sigma$ postupnì: -\:$\indent$dokud $\neg \exists d(\alpha , C) \wedge \alpha\neq\epsilon : \alpha \leftarrow z(\alpha)$ -\:$\indent$dokud $\exists d(\alpha , C) \Rightarrow \alpha \leftarrow d(\alpha , C)$ -\:$\indent$kdy¾ $\alpha = \iota \Rightarrow$ hledané slovo je v textu +\:$\alpha \leftarrow \varepsilon$. +\:Pro $C\in\Sigma$ postupnì: +\:$\indent$Dokud $\neg \exists d(\alpha , C) \wedge \alpha\neq\varepsilon : \alpha \leftarrow z(\alpha)$. +\:$\indent$Jestli¾e $\exists d(\alpha , C) \Rightarrow \alpha \leftarrow d(\alpha , C)$. +\:$\indent$Jestli¾e $\alpha = \iota \Rightarrow$ hledané slovo je v~textu. \endalgo -\s{Alternatíva:} +\s{Alternativa:} \algo \:$k \leftarrow 0$. -\:pro $C\in\sigma$ postupnì: -\:$\indent$dokud $C\neq \iota[k] \wedge k>0: k \leftarrow z(k)$ -\:$\indent$dokud $C=\iota[k] \Rightarrow k++$ -\:$\indent$kdy¾ $k = J \Rightarrow$ hledané slovo je v textu +\:Pro $C\in\Sigma$ postupnì: +\:$\indent$Dokud $C\neq \iota[k] \wedge k>0: k \leftarrow z(k)$. +\:$\indent$Jestli¾e $C=\iota[k] \Rightarrow k++$. +\:$\indent$Jestli¾e $k = J \Rightarrow$ hledané slovo je v~textu. \endalgo -\s{Invariant:} Stav po pøeètení vstupu $\beta$. $\alpha(\beta)$ $=$ nejdel¹í suffix $\beta$, který je prefixem $\iota$. -S invariantu vyplýva korektnost vyhledávací èásti KMP algoritmu. +\s{Invariant:} Stav po pøeètení vstupu $\beta$: $\alpha(\beta)$ $=$ nejdel¹í suffix $\beta$, který je prefixem $\iota$. +Z~invariantu vyplývá korektnost vyhledávací èásti algoritmu KMP. \proof -Dùkaz indukcí. Na zaèátku pro prázdny naètený vstup platí invariant, tedy prázdny suffix $\beta$ je prefixem $\iota$. V kroku $n$ máme naètený vstup $\beta$ a k nìmu naèteme znak $C$. Jestli si odmyslíme $C$, tedy kdy¾ si od jména stavu odmyslíme posledné písmenko, dostaneme znovu jméno stavu. Tak stav, který pasuje na konec vstupu bez toho $C$ je stav, který pasuje na konec pùvodního vstupu, toho o jeden znak krat¹ího. Tím pádem to musí být nìco, co je maximálnì tak dlouhé jako pùvodní stav, u kterého jsme byli, proto¾e to byl nejdel¹í, který pasoval. Staèí procházet postupnì v¹echny stavy, které pasují na konec toho vstupu od nejdel¹ího k nejkrat¹ímu a vzít první, který se dá roz¹íøit o $C$. To je pøesnì to, co algoritmus dìlá. Preto¾e zpìtná funkce øekne nejbli¾¹í krat¹í jméné stavu. Tak¾e algoritmus iteruje pøes stavy, které tam pasují, a¾ najde jeden, který se dá roz¹íøit o $C$ a jeliko¾ iteroval od ty nejdel¹í, tak to je logicky ten nejdel¹í, který tam pasuje. +Dùkaz indukcí. Na zaèátku pro prázdný naètený vstup platí invariant, tedy prázdný suffix $\beta$ je prefixem $\iota$. V~kroku $n$ máme naètený vstup $\beta$ a k~nìmu naèteme znak $C$. Jestli¾e si odmyslíme $C$, tedy kdy¾ si od jména stavu odmyslíme poslední písmenko, dostaneme znovu jméno stavu. Tak stav, který pasuje na konec vstupu bez toho $C$, je stavem, který pasuje na konec pùvodního vstupu, toho o~jeden znak krat¹ího. Tím pádem to musí být nìco, co je maximálnì tak dlouhé jako pùvodní stav, u~kterého jsme byli, proto¾e to byl nejdel¹í, který pasoval. Staèí procházet postupnì v¹echny stavy, které pasují na konec toho vstupu od nejdel¹ího k~nejkrat¹ímu a vzít první, který se dá roz¹íøit o $C$. To je pøesnì to, co algoritmus dìlá, proto¾e zpìtná funkce øekne nejbli¾¹í krat¹í jméno stavu. Tak¾e algoritmus iteruje pøes stavy, které tam pasují, a¾ najde jeden, který se dá roz¹íøit o~$C$, a jeliko¾ iteroval od nejdel¹ího, tak to je logicky ten nejdel¹í, který tam pasuje. \qed -\s{Lemma:} Vyhledávaní dobìhne v èase $\O(S)$. +\s{Lemma:} Vyhledávaní dobìhne v~èase $\O(S)$. \proof -Pro ka¾dý znak vstupního textu mohou nastat dva pøípady. Znak roz¹iruje aktuální prefix, nebo musíme pou¾ít zpìtnou funkci(ypìtnou hranu). Roz¹irování trvá konstantnì mnoho èasu, zatímco zpìtná funkce mu¾e být pro jeden znak volána a¾ $J$-krát. Pøi ka¾dém volání klesne délka aktuálního stavu minimálne o jedna a zároven platí, ¾e kdykoliv stav prodlu¾ujeme, roste právì o jeden znak. Proto v¹ech zkrácení dohromady mu¾e být nejvý¹e tolik, kolik bylo v¹ech prodlou¾ení, t.j. kolik jsme pøeèetli znaku textu. Celkem je tedy poèet krokù lineární vzhledem k délce textu. +Pro ka¾dý znak vstupního textu mohou nastat dva pøípady. Znak roz¹iøuje aktuální prefix, nebo musíme pou¾ít zpìtnou funkci (zpìtnou hranu). Roz¹irování trvá konstantnì mnoho èasu, zatímco zpìtná funkce mu¾e být pro jeden znak volána a¾ $J$-krát. Pøi ka¾dém volání klesne délka aktuálního stavu minimálnì o~jedna a zároveò platí, ¾e kdykoliv stav prodlu¾ujeme, roste právì o~jeden znak. Proto v¹ech zkrácení dohromady mu¾e být nejvý¹e tolik, kolik bylo v¹ech prodlou¾ení, t.j. kolik jsme pøeèetli znakù textu. Celkem je tedy poèet krokù lineární vzhledem k~délce textu. \qed \s{Konstrukce zpìtné funkce:} \algo -\:sestrojíme dopøedné hrany -\:$z( \epsilon ) \leftarrow 0$, $z( \iota [0]) \leftarrow \epsilon $ -\:$\indent$ $\alpha \leftarrow \epsilon$ -\:pro $i = 1$ do $J$ -\:$\indent$$\alpha \leftarrow krok( \alpha , \iota [i])$ -\:$\indent$$z( \iota [0:i+1]) \leftarrow \alpha$ +\:Sestrojíme dopøedné hrany. +\:$z( \varepsilon ) \leftarrow \emptyset$, $z( \iota [0]) \leftarrow \varepsilon $. +\:$\alpha \leftarrow \varepsilon$. +\:Pro $i = 1$ do $J-1$: +\:$\indent$$\alpha \leftarrow krok( \alpha , \iota [i])$. +\:$\indent$$z( \iota [0:i+1]) \leftarrow \alpha$. \endalgo -\s{Vysvìtlení:} V¹imnìte si, ¾e $z(i)$ je pøesnì stav, do nej¾ se dostaneme pøi spu¹tìní na¹eho vyhledávacího algoritmu na øetìzec $\iota [2:i]$, èili na $i$-tý prefix bez prvního písmenka. Proè to tak je? Zpìtná funkce øíká, jaký je nejdel¹í vlastní suffix daného stavu, který je také stavem, zatímco $\alpha$ oznaèuje nejdel¹í suffix textu, který je stavem. Tyto dvì vìci se pøeci li¹í jen v tom, ¾e ta druhá pøipou¹tí i nevlastní suffixy, a právì tomu zabráníme odstranìním prvního znaku. Tak¾e $z()$ získáme tak, ¾e spustíme vyhledávání na èást samotného slova $\iota$. Jen¾e k vyhledávání zase potøebujeme zpìtnou funkci $z$. Proto budeme zpìtnou funkci vytváøet postupne od nejkrat¹ích prefixu. Zøejmì $z(1) = \epsilon$. Pokud ji¾ máme $z(i)$, pak výpoèet $z(i+1)$ odpovídá spu¹tení automatu na slovo délky i a pritom budeme zpìtnou funkci potøebovat jen pro stavy délky $i$ nebo men¹í, pro které ji ji¾ máme hotovou. +\s{Vysvìtlení:} V¹imnìte si, ¾e $z(i)$ je pøesnì stav, do nìho¾ se dostaneme pøi spu¹tìní na¹eho vyhledávacího algoritmu na øetìzec $\iota [2:i]$, èili na $i$-tý prefix bez prvního písmenka. Proè to tak je? Zpìtná funkce øíká, jaký je nejdel¹í vlastní suffix daného stavu, který je také stavem, zatímco $\alpha$ oznaèuje nejdel¹í suffix textu, který je stavem. Tyto dvì vìci se pøeci li¹í jen v~tom, ¾e ta druhá pøipou¹tí i nevlastní suffixy, a právì tomu zabráníme odstranìním prvního znaku. Tak¾e $z()$ získáme tak, ¾e spustíme vyhledávání na èást samotného slova $\iota$. Jen¾e k~vyhledávání zase potøebujeme zpìtnou funkci $z$. Proto budeme zpìtnou funkci vytváøet postupne od nejkrat¹ích prefixu. Zøejmì $z(1) = \varepsilon$. Pokud ji¾ máme $z(i)$, pak výpoèet $z(i+1)$ odpovídá spu¹tení automatu na slovo délky i a pritom budeme zpìtnou funkci potøebovat jen pro stavy délky $i$ nebo men¹í, pro které ji ji¾ máme hotovou. -Navíc nemusíme pro jednotlivé prefixy spou¹tìt výpoèet v¾dy znovu od zaèátku, proto¾e $(i+1)$-ní prefix -je prodlou¾ením $i$-tého prefixu o jeden znak. Staèí tedy spustit algoritmus na celý øetìzec $\iota[1:J]$ a sledovat, jakými stavy bude procházet. A to budou pøesnì hodnoty zpìtné funkce. Vytvoøení zpìtné funkce se tak nakonec zredukovalo na jediné vyhledávání v textu o délce $J-1$, a proto pobe¾í v case $\O(J)$. Èasová slo¾itost celého algoritmu tedy bude $\O(S+J)$. +Navíc nemusíme pro jednotlivé prefixy spou¹tìt výpoèet v¾dy znovu od zaèátku, proto¾e $(i+1)$-ní prefix +je prodlou¾ením $i$-tého prefixu o~jeden znak. Staèí tedy spustit algoritmus na celý øetìzec $\iota[1:J]$ a sledovat, jakými stavy bude procházet. A to budou pøesnì hodnoty zpìtné funkce. Vytvoøení zpìtné funkce se tak nakonec zredukovalo na jediné vyhledávání v~textu o~délce $J-1$, a proto pobe¾í v case $\O(J)$. Èasová slo¾itost celého algoritmu tedy bude $\O(S+J)$. -\h{Algoritmus Rabin \& Karp} -Tenhle algoritmus funguje tak, ¾e porovnává hash hledaného øetìzce s hashem aktuálního podøetìzce v textu a aktuální podøetìzec porovná se vzorkem pouze v pøípadì, kdy¾ mají shodný hash. Kdy¾ si zvolíme tu správnou hashovací funkci, budeme moci vypoèítat hash následujíciho podøetìzce na základe hashe toho aktuálního. Jako hashovací funkci $h: \sum^J \rightarrow Z$ pou¾ijeme následující: $h(x_{0},...,x_{j-1}) = ( \sum_{i=1}^J x_{i}.p^{J-i})$ $mod$ $N$, kde $N$ je velikost prostoru do kterého hashujeme. Jak zjistíme hash následujícího podøetìzce? +\h{Algoritmus (Rabin, Karp)} +Tenhle algoritmus funguje tak, ¾e porovnává hash hledaného øetìzce s~hashem aktuálního podøetìzce v~textu a aktuální podøetìzec porovná se vzorkem pouze v~pøípadì, kdy¾ mají shodný hash. Kdy¾ si zvolíme tu správnou hashovací funkci, budeme moci vypoèítat hash následujíciho podøetìzce na základe hashe toho aktuálního. Jako hashovací funkci $h: \Sigma^J \rightarrow {\bb Z}$ pou¾ijeme následující: +$$h(x_{0},...,x_{J-1}) = ( \sum_{i=0}^{J-1} x_{i}.p^{J-i}) \bmod N,$$ +kde $N$ je velikost prostoru, do kterého hashujeme. Jak zjistíme hash $h{'}$ následujícího podøetìzce? \itemize\ibull \:$h = x_{0}.p^{J} + x_{1}.p^{J-1} + ... + x_{J-1}.p^{1}$ -\:$h1 = x_{1}.p^{J} + x_{2}.p^{J-1} + ... + x_{J}.p^{1}$ -\:$h1 = (h - x_{0}.p^{J}).p + x_{J}.p^{1}$ +\:$h^{'} = x_{1}.p^{J} + x_{2}.p^{J-1} + ... + x_{J}.p^{1}$ +\:$h^{'} = (h - x_{0}.p^{J}).p + x_{J}.p^{1}$ \endlist -Èasová slo¾itost je v nejlep¹ím pøípadì lineární vzhledem k délce textu, zatímco nejhor¹í pøípad mú¾e trvat a¾ $\O(JS)$. +Tady mù¾eme vidìt, ¾e hash následujícího øetìzce lze pøepoèítat na základì toho pøedchozího v konstantním èase. Èasová slo¾itost je v nejlep¹ím pøípadì lineární vzhledem k~délce textu, zatímco nejhor¹í pøípad mù¾e trvat $\O(JS)$. \bye