diff --git a/12-dp/obdelnik-12.py b/12-dp/obdelnik-12.py new file mode 100644 index 0000000000000000000000000000000000000000..dab435116364966051be40b19dceeedc00463b24 --- /dev/null +++ b/12-dp/obdelnik-12.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +# Počet dláždění obdélníka 1×n kostkami 1×1 a 1×2 + +# Rekurzivní funkce s exponenciální složitostí +def d(n): + if n <= 1: + return 1 + return d(n-1) + d(n-2) + +# Všimli jsme si, že d() počítá spoustu podproblémů opakovaně, +# tak jsme dodělali kešování známých mezivýsledků. Tím se časová +# složitost zlepšila na O(n), ale chvíli trvá to dokázat. +pamet = { 0: 1, 1: 1 } +def d2(n): + if n not in pamet: + pamet[n] = d2(n-1) + d2(n-2) + return pamet[n] + +# Tady počítáme tytéž podproblémy od nejmenšího k největšímu. +# Rekurzi jsme nahradili obyčejným cyklem, složitost je evidentně O(n). +def d3(n): + D = [1, 1] + [0]*n + for i in range(2, n+1): + D[i] = D[i-1] + D[i-2] + return D[n] + +# Konečně jsme si všímli, že není potřeba pamatovat si všechny +# mezivýsledky, ale stačí předchozí dva. Tím jsme snížili prostorovou +# složitost na konstantní. +def d4(n): + a = b = 1 + for i in range(2, n+1): + a, b = a+b, a + return a diff --git a/12-dp/obdelnik-1k.py b/12-dp/obdelnik-1k.py new file mode 100644 index 0000000000000000000000000000000000000000..20322a1326277261bb395a49bae98df58993564a --- /dev/null +++ b/12-dp/obdelnik-1k.py @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +# Počet dláždění obdélníka 1×n kostkami 1×k (kde k je libovolné) + +def d(n): + if n == 0: + return 1 + return sum(d(i) for i in range(n)) diff --git a/12-dp/rozklad-na-slova.py b/12-dp/rozklad-na-slova.py new file mode 100644 index 0000000000000000000000000000000000000000..b4fd1aba889372d2574afa295e18241967754c4c --- /dev/null +++ b/12-dp/rozklad-na-slova.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +# Rozklad řetězce na posloupnost slov ze slovníku. + +# Načítání slovníku ze souboru + +slovnik = set() + +def nacti_slovnik(): + with open('slovnik') as f: + for slovo in f: + slovnik.add(slovo.strip()) + +# První pokus: rekurzivní rozkládání. Zkoušíme všechny možnosti, +# jaké slovo může být první, a pro každou z nich rekurzivně zjistíme, +# zda se zbytek řetězce dá rozložit. V nejhorším případě to trvá +# exponenciálně dlouho (například pro slovník {"a","aa"} a řetězec +# ze samých a-ček je tato úloha ekvivalentní s rozkladem obdélníka, +# který jsme už zkoumali). +def rozloz(retezec): + if retezec == "": + return True + for i in range(len(retezec)+1): + if retezec[:i] in slovnik and rozloz(retezec[i:]): + return True + return False + +# Všimli jsme si, že všechny podproblémy jsou suffixy původního řetězce, +# takže jako parametr místo řetězce předáváme pozici v původním řetězci +# (začátek suffixu). Také bychom si mohli všimnout (ale ještě jsme toho +# nevyužili), že možných podproblémů je málo a začít kešovat. +def rozloz2(retezec): + + def roz(p): + """Rozlož retezec[p:]""" + if p == len(retezec): + return True + return any(retezec[p:q] in slovnik and roz(q) for q in range(p+1, len(retezec)+1)) + + return roz(0) + +# Podproblémy řešíme od nejkratšího suffixu k nejdelšímu, rekurzi jsme +# nahradili cyklem. Časová složitost činí O(n³). +def rozloz3(retezec): + + n = len(retezec) + umim = [None]*(n+1) + umim[n] = True + + for p in range(n-1, -1, -1): + umim[p] = any(retezec[p:q] in slovnik and umim[q] for q in range(p+1, n+1)) + + return umim[0] + +# A teď nás zajímá nejen, jestli rozklad existuje, ale také jak nějaký konkrétní +# rozklad vypadá. Pro každý suffix si zapamatujeme, které slovo (stačí jeho délka) +# fungovalo jako první v suffixu. Podle toho pak rozklad sestrojíme. +def rozloz4(retezec): + + n = len(retezec) + umim = [None]*(n+1) + umim[n] = 0 + + # Hlavní výpočet + for p in range(n-1, -1, -1): + for q in range(p+1, n+1): + if retezec[p:q] in slovnik and umim[q] is not None: + umim[p] = q-p + + # Rekonstrukce rozkladu + if umim[0] is None: + return None + else: + rozklad = [] + p = 0 + while p < n: + rozklad.append(retezec[p:p+umim[p]]) + p += umim[p] + return rozklad + + return umim[0] + +nacti_slovnik()