Zadanie

Ak máte akékoľvek otázky ohľadom tejto úlohy napíšte Michalovi “Žabovi” Anderlemu na

V minulej sérii sme sa prvýkrát stretli s regulárnymi výrazmi v tejto úlohe1. Opäť sa k nim však vrátime. Ako sme videli, pomocou regulárnych výrazov sa dajú hľadať alebo akceptovať rôzne zaujímavé reťazce, ktoré spĺňajú isté vlastnosti. Napríklad môžete ľahko napísať regulárny výraz, ktorý vám overí, či je zadaný reťazec e-mailová adresa. Niekedy však potrebujeme overovať aj zložitejšie vlastnosti. Napríklad deliteľnosť.

A práve o tom bude celá táto séria. Budeme sa snažiť akceptovať reťazce cifier 0 až 9, ktoré reprezentujú čísla a naviac musia byť deliteľné nejakým konkrétnym číslom. Deliteľnosť niektorými číslami sa overuje ľahko, napr. 2, 4, 5, 8 alebo 10. Inými je to o niečo zložitejšie. V rámci tohto zadania postupne vytvoríme regulárny výraz akceptujúce čísla deliteľné troma. Nemusíte sa však báť, pôjdeme na to krok po kroku. Začneme tým, že si pripomenieme ako také regulárne výrazy vytvárame.

Základy použitia regulárnych výrazov

Pre jednoduchosť sa dohodneme, že všetky objekty, ktoré budeme hľadať, budú reťazce tvorené obmedzenou množinou znakov. Povolené znaky budú len písmená anglickej abecedy a cifry ("a"-"z", "A"-"Z", "0"-"9").2

Vyhľadávať budeme tak, že napíšeme regulárny výraz – teda nejakú vzorku (pattern). Vzorka popisuje ako vyzerajú reťazce, ktoré nás zaujímajú. Presnejšie, o každom reťazci vieme povedať, či vzorke zodpovedá (matches the pattern) a naším cieľom pri vyhľadávaní bude napísať vzorku tak, aby jej zodpovedali práve tie reťazce, ktoré chceme nájsť a žiadne iné. Ako uvidíme nižšie, vo vzorke sa budú môcť vyskytovať aj niektoré iné znaky ako v reťazcoch.

Teraz si popíšeme základy tvorby vzoriek – teda povieme si, z čoho sa taká vzorka môže skladať a ktoré reťazce jej potom zodpovedajú.

  • Základným stavebným kameňom vzoriek sú samotné znaky z vyššie uvedeného zoznamu. Vzorke tvorenej jediným znakom zodpovedá reťazec tvorený dotyčným znakom a nič iné.

  • Základná operácia so vzorkami je zreťazenie. Keď napíšeme dve vzorky za sebou, dostaneme novú vzorku. Tej zodpovedajú reťazce zložené z dvoch častí, pričom prvá zodpovedá prvej vzorke a druhá druhej. Zreťaziť samozrejme môžeme ľubovoľne veľa vzoriek. Napr. vzorke "jablko" zodpovedá len reťazec "jablko".

  • Vzorky môžeme uzatvárať do obyčajných zátvoriek. Napr. vzorke "(jablko)" zodpovedá len reťazec "jablko" a nič iné.

  • Logický or (alebo) je označovaný symbolom "|". Ak ním spojíme dve vzorky, dostaneme novú, ktorej zodpovedajú aj reťazce zodpovedajúce prvej, aj reťazce zodpovedajúce druhej vzorke. Napr. vzorke "jabl(k|ck)o" zodpovedajú reťazce "jablko" a "jablcko".

    Zreťazenie má vyššiu prioritu ako or. Napr. vzorke "aaa|bbb" zodpovedajú len reťazce "aaa" a "bbb".

  • Inú možnosť, ako dať vo vzorke na výber predstavuje množina znakov, ktoré sa na danom mieste môžu vyskytnúť. Tú zapisujeme "[znaky]". Napríklad vzorke "jablk[oa]" zodpovedajú reťazce "jablko" a "jablka".

    Množina všetkých dostupných znakov sa skrátene zapisuje ".". Teda vzorke "j....a" zodpovedá každý 6-znakový reťazec začínajúci na "j" a končiaci na "a".

    Zložitejšie množiny znakov môžeme zapísať pomocou rozsahov, napr. "[a-z0-9]" je ľubovoľné malé písmeno alebo číslica. Ak popis množiny znakov začína znakom "^", znamená to negáciu: danej množine zodpovedajú všetky znaky okrem vymenovaných. Napríklad vzorke "[a-z][^a-zA-Z]" zodpovedajú všetky dvojznakové reťazce, ktorých prvý znak je malé písmeno a druhý znak nie je ani malé, ani veľké písmeno.

  • Ak chceme, aby sa mohla časť vzorky v reťazci zopakovať, použijeme kvantifikátor. Základné kvantifikátory sú "?" (nulakrát alebo jedenkrát) a "*" (ľubovoľne veľakrát, aj nulakrát).

    Napríklad vzorka "jablc?ko" je ekvivalentná so vzorkou "jabl(k|ck)o". Vzorke "pe*s" zodpovedajú okrem iného reťazce "ps", "pes", "pees"… Vzorke "(ab)*c" nezodpovedá reťazec "aabbc", len reťazce "c", "abc", "ababc", atď.

    Pri opakovaní vzorky jej nemusí zakaždým zodpovedať ten istý reťazec. Napríklad vzorke "j.*a" zodpovedá nielen reťazec "jxxxa", ale aj reťazec "jahoda".

    Vzorke "<[^>]*>" zodpovedá každý XML tag – reťazec začína znakom "<", za ním nasleduje ľubovoľne veľa znakov iných od ">" a za nimi nasleduje znak ">".

Súťažná úloha

Táto úloha má niekoľko podúloh. V každej podúlohe budete mať slovne popísanú nejakú skupinu reťazcov. Vaším cieľom bude napísať vzorku, ktorej zodpovedajú všetky popísané reťazce a žiadne iné!

  1. (1 bod) Začneme zľahka. Napíšte regulárny výraz, ktorý akceptuje čísla deliteľné piatimi, ktoré nezačínajú zbytočnými nulami. To znamená, že má akceptovať reťazce "0", "15", ale nemá akceptovať reťazce "015" alebo "11111".

V ďalších podúlohách budeme riešiť problémy zlodejov z filmu Sám doma. Stoja na začiatku vysokých schodov, dá sa povedať že nekonečných, a chcú vyjsť hore. Každým krokom môžu buď výjsť o schod vyššie (1) alebo jeden schod prekročia, čím sa dostanú o dva schody vyššie (2). Ich cesta hore schodmi sa teda dá zapísať ako reťazec z "1" a "2". Nemusia dokonca výjsť ani úplne navrch, je možné, že na niektorom schode zastanú a zostanú tam proste stáť.

Ak by sme chceli napísať regulárny výraz, ktorý akceptuje všetky reťazce, ktoré zodpovedajú ich cestám hore schodmi, vyzeral by takto: "[12]*". Kevin však na nich prichystal pascu a niektoré schody natrel lepidlom. Tým sa chcú zlodeji väčšinou vyhnúť, lebo ak na ne stúpia, prilepia sa a ostanú tam stáť.

  1. (2 body) Kevin potrel lepidlom tretí a šiesty schod. Napíšte regulárny výraz, ktorý akceptuje všetky cesty po schodoch, pri ktorých sa zlodej nenalepí na žiadny schod a vyjde aspoň na siedmy schod. To znamená, že treba akceptovať reťazce "1121222" alebo "11212", ale nemôžete akceptovať napríklad "111", "22" alebo "2112".

  2. (3 body) Tentokrát sú lepidlom natreté všetky schody, ktorých číslo je deliteľné tromi. Vytvorte regulárny výraz, ktorý akceptuje všetky cesty hore schodmi, pri ktorých sa zlodej nikdy neprilepí. To znamená, že reťazec "11222212" nemáte akceptovať, lebo zlodej sa prilepí na schode číslo \(6\) (po tom, čo urobí "1122"). Dobré reťazce sú napríklad "11" alebo "1121212".

  3. (2 body) Lepidlom sú stále natreté schody deliteľné tromi. Lepidlo je však neviditeľné a zlodeji o ňom nevedia. Preto sa počas toho, ako išli hore nalepili na jednom schode. Spravte regulárny výraz, ktorý popisuje práve tie cesty hore schodmi, ktoré skončia na schode deliteľnom troma. Správne reťazce sú napríklad "112122" alebo "111", ale nie "2212", lebo pri ňom sa zlodej nenalepil a ani "22111122", lebo zlodej sa nalepil už na schode šesť, po tom čo prešiel "2211" a teda nemohol pokračovať.

  4. (1 bod) Zadanie je rovnaké ako v podúlohe d), akurát zlodej má na sebe viacero vrstiev ponožiek a keď sa nalepí na nejaký schod, najvrchnejšiu vrstvu si môže vyzuť a pokračovať v ceste. Skončiť ale musí aj tak na schode deliteľnom tromi. To znamená, že reťazec "22111122" je správny, v poriadku ale nie je reťazec "12212221", lebo takáto cesta neskončí na zalepidlovanom schode.

Odteraz to už bude veľmi podobné podúlohe e). Budeme chcieť také cesty hore schodmi, ktoré skončia na schode deliteľnom tromi, pričom medzitým sa môže prilepiť koľkokrát chce. Uvedomme si, že to zodpovedá číslam skladajúcim sa z cifier "1" a "2", ktorých ciferný súčet je deliteľný troma.

  1. (1 bod) Hľadáme regulárny výraz akceptujúci čísla s ciferným súčtom deliteľným troma, ktoré môžu naviac obsahovať aj cifru "0". Napríklad teda reťazec "01200201".

  2. (3 body) A pokúsme sa pridať aj zvyšné cifry. Hľadáme čísla s ciferným súčtom deliteľným trojkou, ktoré môžu obsahovať ľubovoľnú cifru "0""9".

  3. (2 body) Regulárny výraz z podúlohy g) je takmer presne to čo hľadáme – čísla deliteľné tromi. Lebo práve tie majú ciferný súčet deliteľný tromi. Jediné, čo je naviac je, že dovoľujeme, aby číslo začínalo prebytočnými nulami. Pokúste sa opraviť tento nedostatok a vytvorte regulárny výraz, ktorý naozaj akceptuje iba čísla deliteľné troma.

V riešení týchto úloh neodovzdajte iba hľadaný regulárny výraz, ale vysvetlite, ako ste našli daný regulárny výraz a prečo vášmu regulárnemu výrazu zodpovedajú iba správne reťazce a žiadne iné.

Pomôcka

Existuje množstvo online nástrojov, kde si viete vyskúšať prácu s regulárnymi výrazmi a testovať riešenia tejto úlohy. Medzi ne patrí napríklad aj https://regex101.com/. Ak na tejto stránke napíšete do okienka “regular expression” svoju vzorku a do okienka “tesst string” nejaký text, stránka vo vami zadanom texte , ktorý zadanej vzorke zodpovedá. Vyskúšajte si napr. zadať vzorku "jabl(k|ck)o" a text "mak jablko jahoda". Ak si chcete vynútiť test, či celý zadaný text zodpovedá vašej vzorke, dopíšte na jej začiatok znak "^" a na jej koniec znak "$". Tieto dva špeciálne znaky predstavujú začiatok a koniec celého textu, v ktorom hľadáme.


  1. Odporúčam si prečítať vzorové riešenie, môže vám to pomôcť pri tejto úlohe.

  2. V skutočných regulárnych výrazoch môžeme samozrejme používať aj všetky ostatné znaky vrátane medzier. Našim obmedzením sa však vyhneme vysvetľovaniu mnohých technických detailov.

Podúloha a)

Prvou podúlohou bolo napísať regulárny výraz, ktorý akceptuje všetky reťazce predstavujúce čísla deliteľné \(5\). Vieme, že číslom 5 sú deliteľné tie čísla, ktoré majú na konci cifru "0" alebo "5". Podľa zadania si však máme dať pozor na to, aby nami akceptované čísla nemali na začiatku zbytočné nuly. Môžeme napísať regulárny výraz, ktorý bude mať na prvom mieste nenulovú cifru, za ktorou bude nasledovať niekoľko ľubovoľných cifier a následne cifra 0 alebo 5. Tento výraz vyzerá takto: "[1-9][0-9]*[05]".

Samozrejme, takýto výraz neakceptuje dve výnimky – číslo 0 a číslo 5. Tieto pridáme pomocou "|", takže výsledný regulárny výraz má tvar "([1-9][0-9]*[05])|0|5".

Delenie číslom 5 bolo ľahké. Museli sme sa trochu pohrať s nulami na začiatku, ale inak nám stačilo splniť jedinú podmienku – 0 alebo 5 na konci čísla. Aj pre čísla deliteľné 3 platí jednoduchá podmienka: ciferný súčet týchto čísel musí byť deliteľné troma. Takáto podmienka sa však nedá zapísať pomocou regulárnych výrazov takým jednoduchým spôsobom. Nasledujúce podúlohy preto slúžili na to, aby sme postupne mohli vytvárať finálne riešenie.

Podúloha b)

V tejto podúlohe bolo treba napísať regulárny výraz, ktorý obsahuje iba cifry 1 a 2 predstavujúce chodenie po schodoch. Má pritom platiť, že tento regulárny výraz popisuje všetky cesty na schod vyšší ako šiesty, ktoré nestúpia na schod 3 ani na schod 6.

Keď zlodej stojí na schode číslo 0 (teda na spodku schodišťa), má dve možnosti. Buď spraví krok dĺžky 1 alebo dĺžky 2. Pozrime sa, čo sa stane ak najskôr spraví krok dĺžky 1. Týmto krokom sa dostane na schod 1, z ktorého ale nemôže spraviť krok dlhý 2, lebo by sa dostal na zalepidlovaný schod 3. Musí teda spraviť krok dĺžky 1, čím sa ocitne na schode číslo 2. Následne musí prekročiť schod 3 pomocou dlhšieho kroku dĺžky 2, čím sa dostane na schod 4. Odtiaľ má opäť iba jednu možnosť, krok dĺžky 1, lebo dlhším krokom by sa ocitol na schode 6. No a z piateho schodu musí spraviť krok dĺžky 2, aby prekročil zalepidlovaný schod. V tomto momente už pred ním nie sú žiadne zalepené schody a môže pokračovať ľubovoľne. Tento postup vieme zapísať ako regulárny výraz "11212[12]*".

Čo ak sa však na začiatku rozhodne spraviť krok dĺžky 2? Ocitne sa na schode 2, čo znamená, že opäť musí spraviť krok dĺžky 2, aby prekročil schod 3. Tak ako predtým, keď sa ocitol na schode 4, musí spraviť krok dĺžky 1 a potom dĺžky 2. Tým sa dostane na schod 7, odkiaľ je to opäť bezpečné. Tento postup popisuje regulárny výraz "2212[12]*".

Vidíme, že kým sa zlodej dostal na siedmy schod, rozhodoval sa iba na úplnom začiatku a zvyšok cesty bol presne určený. Výsledný regulárny výraz teda dostaneme spojením týchto dvoch regulárnych výrazov, čo vieme urobiť napríklad tak, že ich oddelíme "|". Ak sa však trochu zamyslíme, nájdeme aj kratší spôsob: "(11|2)212[12]*".

Podúloha c)

Všimnime si, že v podúlohe b) sme mali na výber len na úplnom začiatku, keď sme boli na schode číslo 0 a potom až keď sme sa dostali na schod 7, kde už neboli žiadne schody potreté lepidlom. Uvedomme si, že schody môžeme rozdeliť na tri skupiny podľa toho, či ich číslo dáva po delení 3 zvyšok 0, 1 alebo 2.

Takisto si všimnime, že ak stojíme na schode, ktorého zvyšok po delení 3 je 1, tak nemôžeme spraviť krok dĺžky 2, lebo by sme sa dostali na schod pokrytý lepidlom. Musíme teda spraviť krok dĺžky 1, čím sa dostaneme na schod, ktorého zvyšok po delení 3 je 2. Ak stojíme na schode so zvyškom 2, musíme spraviť krok dĺžky 2, ktorým sa dostaneme na schod so zvyškom 1.

Ak teda ako prvý spravíme krok dlžky 1, následne budeme musieť striedavo robiť kroky dlžky 1 a 2, lebo iba tak nikdy nestúpime na schod deliteľný číslom 3. Ak ako prvý spravíme krok dlžky 2, budeme musieť dookola opakovať postupnosť krokov 2 a 1. Toto vieme ľahko zapísať pomocou regulárneho výrazu ako "1(12)*1?|2(21)*2?". Na koniec každej časti sme museli pridať ešte jeden znak s otáznikom, aby reťazce začínajúce na 1 mohli končiť 1 a reťazce začínajúce 2 mohli končiť 2. Bez nich by sme napríklad neakceptovali reťazec "11" alebo "2212", ktoré sú oba správne.

Otázkou je, či by náš regulárny výraz mal akceptovať aj prádzny reťazec. Zo zadania to nie je úplne jasné1. Môžeme sa teda dohodnúť, že prázdny reťazec za cestu považovať nebudeme. Ak by sme ho tam veľmi chceli mať, ľahko ho môžeme pridať jedným or-om.

Podúloha d)

V tejto podúlohe máme napísať regulárny výraz, ktorý zodpovedá tomu, že zlodeji nejakú dobu kráčali po schodoch a potom stúpili na schod deliteľný 3, prilepili sa a ostali stáť. Všimnime si, že táto podúloha silno súvisí s podúlohou c). Keďže sa zlodeji nemohli prilepiť počas cesty, museli sa nejakú dobu vyhýbať schodom s lepidlom až kým to potom na konci nepokazili. A časť ich cesty, kým sa polepeným schodom úspešne vyhýbali, predsa popisuje regulárny výraz z podúlohy c).

Ostáva zistiť, čo musí zlodej spraviť, aby stúpil na schod, ktorého číslo je deliteľné 3. Ak stojí na schode, ktorého zvyšok po delení 3 je 1, tak ak spraví kroky "11" alebo "2", skončí na schode s lepidlom. Ak schod, na ktorom stojí má zvyšok 2, tak sa zalepí ak spraví krok "1". Predstavme si, že zlodej išiel najskôr podľa regulárneho výrazu "1(12)*". Ocitol sa teda na schode so zvyškom 1 a preto musel spraviť buď postupnosť "11" alebo "2". Ak išiel najskôr podľa "2(21)*" tak sa ocitol na schode so zvyškom 2 a teda musel spraviť krok "1".

V podúlohe c) sme však museli pridať aj reťazce "1?" a "2?" aby sme ošetrili všetky možnosti. A tu ich tiež istým spôsobom potrebujeme. V prvom prípade to nemusíme riešiť, lebo ak zlodej po ceste typu "1(12)*" spraví ešte na konci krok 1, tak potom musí spraviť opäť krok 1 aby sa prilepil, čo sme pokryli v možnosti "1(12)*11". V druhom prípade však neriešime situáciu, ak zlodej po ceste typu "2(21)*" urobil krok 2 a potom sa chce prilepiť. Okrem možnosti "1" teda musíme za "2(21)*" pridať aj možnosť "22", ktorá zodpovedá presne tomuto prípadu. Spojením tohto všetkého dostaneme regulárny výraz "1(12)*(11|2)|2(21)*(1|22)".

Podúloha e)

Asi najľahšia podúloha, ak sme vyriešili podúlohu d). Ak sa zlodej prilepí, musí sa mu to stať na schode deliteľnom troma. Ak si teda vyzuje ponožky a pokračuje ďalej, je to to isté, ako keby začínal na schode 0 (ktorý je tiež deliteľný 3). Takže celá jeho cesta je len viacero ciest, pri ktorých sa vyhýba schodom deliteľným 3, až kým sa nenalepí a potom začína akoby odznova. Toto vieme docieliť pridaním "*" na koniec výrazu z podúlohy d), teda "(1(12)*(11|2)|2(21)*(1|22))*". Takto sme povolili aj prázdny reťazec. Ak by sme ho chceli zakázať, môžeme riešenie podúlohy d) zopakovať dvakrát a "*" dať iba za druhé opakovanie.

Podúloha f)

Ak zlodej zostane chvíľu stáť na mieste, nič sa na jeho situácii nezmení. Ak by sme odstránili nuly zo vstupného reťazca, musíme dostať reťazec, ktorý je vhodný pre podúlohu e). To znamená, že chceme robiť to isté, čo v podúlohe e) akurát hocikde môžeme pridať ľubovoľne veľa núl. Pridanie ľubovoľného množstva núl nám zabezpečí reťazec "0*" a jediné čo potrebujeme spraviť, je vložiť ho na všetky možné miesta reťazca z podúlohy e).

Je tu však ešte jeden háčik. Až po podúlohu d) sme státie na schode 0 (teda prázdny reťazec) netolerovali, teraz by sme však chceli akceptovať aj reťazce zložené iba zo samých núl. Preto na koniec nášho výrazu pridáme ešte "|0*".

Výsledok vyzerá trochu neprehľadne, ale uvedomte si, že je to naozaj len riešenie z podúlohy e) s množstvom núl, ktoré nič nerobia (a pridaným špeciálnym prípadom na konci). Možno by sme vedeli nejaké nuly ušetriť, keby sme náš regulárny výraz poriadne rozanalyzovali, jednoduchšie a bezpečnejšie však bude vložiť "0*" medzi každé dva znaky.

"(0*10*(0*10*20*)*(0*10*10*|0*20*)|0*20*(0*20*10*)*(0*10*|0*20*20*))*|0*"

Podúloha g)

Táto podúloha vyzerá na prvý pohľad najodstrašujúcejšie. Bolo ťažké poradiť si s ciframi 0, 1 a 2 a zrazu nám pribudne ďalších sedem. Budeme musieť urobiť tú istú úvahu celú od začiatku, akurát s toľkými rôznymi možnosťami? Našťastie nie.

Pozrime sa napríklad na cifru 3. Ak stojíme na zalepenom schode a posunieme sa o tri schody vyššie, opäť sa ocitneme na schode pokrytom lepidlom. Čo je v podstate to isté, ako keby sme zostali stáť na mieste. Áno, posunuli sme sa o tri schody vyššie, ale nás väčšinou nezaujíma ako vysoko sme, len to, kde pred nami sú ďalšie zalepené schody, poprípade to, aký zvyšok po delení 3 dáva schod na ktorom stojíme. A toto sa pri cifre 3 nemení.

Skúsme na to hľadieť nie cez schody, ale cez čísla. Hľadali sme čísla, ktorých ciferný súčet je deliteľný troma. Pridanie ľubovoľného množstva cifier 3 nám síce zmení ciferný súčet, ale nie jeho deliteľnosť číslom 3. Cifra 3 sa preto správa úplne rovnako ako cifra 0. Môžeme ju dať kam len chceme a koľkokrát len chceme. A toto isté platí aj pre cifry 6 a 9, ktoré sú tiež deliteľné troma.

Teraz snáď už začínate tušiť, ako to bude pokračovať. Ak si zoberieme cifru 4, použitie tejto cifry má na zvyšok po delení 3 rovnaký efekt, ako použitie cifry 1. A takisto cifra 7 má rovnaký efekt. To znamená, že tieto cifry sú navzájom zameniteľné. Všade, kde sme v pôvodnom regulárnom výraze použili cifru 1, môžeme teraz použiť cifry 4 alebo 7 bez toho, aby sa čokoľvek pokazilo. No a samozrejme, cifry 5 a 8 sú rovnaké ako cifra 2.

Dostať regulárny výraz, pre podúlohu g) je potom vskutku jednododuché. Zoberieme si výraz z podúlohy f) a každý výskyt "0" nahradíme "[0369]", každý výskyt "1" nahradíme "[147]" a každý výskt "2" nahradíme "[258]". To boli totiž skupiny rovnako sa správajúcich cifier, pri ktorých je jedno, ktorú z nich použijeme.

Výsledný reťazec je už taký dlhý, že sme ho museli rozdeliť do viacerých riadkov. Opäť to však nie je nič strašné, ak si uvedomíme, akým spôsobom vznikol.

"([0369]*[147][0369]*([0369]*[147][0369]*[258][0369]*)*
([0369]*[147][0369]*[147][0369]*|[0369]*[258][0369]*)|
[0369]*[258][0369]*([0369]*[258][0369]*[147][0369]*)*
([0369]*[147][0369]*|[0369]*[258][0369]*[258][0369]*))*|
[0369]*"

Podúloha h)

Zostáva už len odstrániť prebytočné nuly na začiatku. Našťastie, toto sa dá urobiť pomerne jednoducho. Najprv úplne zakážeme, aby na začiatku čísla bola ktorákoľvek z cifier 0, 3, 6, 9. To urobíme tak, že zmažeme "|[0369]*" z konca výrazu a v oboch možnostiach veľkého or-u zmažeme zo začiatku "[0369]*".

Tu by mohol vzniknúť problém, keďže veľký or sa v reťazci môže opakovať a my chceme zmazať "[0369]*" iba zo začiatku prvého opakovania. Našťastie pre nás, aj na konci každej možnosti nášho veľkého or-u máme "[0369]*". O tom, čo je na začiatku druhého opakovania, teda môžeme prehlásiť, že je to v skutočnosti ešte na konci prvého opakovania. To znamená, že náš upravený výraz naozaj robí to, čo pôvodné riešenie podúlohy g), akurát nedovoľuje, aby číslo začínalo niektorou z cifier 0, 3, 6, 9.

Teraz naspäť povolíme, aby reťazec začínal niekoľkými (možno žiadnymi) ciframi 0, 3, 6 alebo 9, pričom ale prvá nemôže byť nula. Tomu zodpovedá regulárny výraz "[369][0369]*|", ktorý (v zátvorke) stačí napísať pred zvyšok nášho výrazu. Nakoniec ešte musíme pridať jeden špeciálny prípad – samotné číslo 0.

"([369][0369]*|)
([147][0369]*([0369]*[147][0369]*[258][0369]*)*
([0369]*[147][0369]*[147][0369]*|[0369]*[258][0369]*)|
[258][0369]*([0369]*[258][0369]*[147][0369]*)*
([0369]*[147][0369]*|[0369]*[258][0369]*[258][0369]*))*|0"

  1. Ak sa zlodej nepohne, určite sa neprilepí – ale dá sa to považovať za cestu hore schodmi?

Diskusia

Tu môžte voľne diskutovať o riešení, deliť sa o svoje kusy kódu a podobne.

Pre pridávanie komentárov sa musíš prihlásiť.