Üks võimalik rekursiivne lahendus:
Otsime fraasi f anagramme nii, et lahutame fraasist sõnu maha ja otsime siis (fraas-sõna) anagramme.
Defineerime funktsiooni X mis võtab sisse fraasi f ja ettearvutatud sõnastiku S
- vaatame, kas f on ise S sees sisalduv sõna
- kui on, siis on üks anagramm leitud. jätame meelde!
- teeme tsükli üle kõikide sõnastiku S sõnade s
- leiame jääk-fraasi f-s
- arvutame välja jäägile vastava uue sõnalisti: selles on sõnad, mis võivad olla (f-s) anagrammiks või anagrammi osadeks
- kutsume X välja, fraasiks on siis (f-s) ja sõnastikuks uus sõnastik
Rekursiooni lõpp ehk baas on see, kui sisendfraas jääb nii lühikeseks, et temast enam anagrammi leida ei saa.
Kui sisend on "paks kurinurm", siis käik on umbes:
X (lõbuskurinurm) = "lõbus" + X (kurinurm) = "lõbus" + "kuri" + X(nurm), ja kuna viimane on sõna, siis anagramm ongi "lõbus kuri nurm".
Veel:
- peame alati teadma rekursiooni hetke taset. Me ei taha liiga sügavale minna / liiga mitmeosalisi anagramme leida) ja võimaliku anagrammi seni leitud osiseid.
- peame edasi andma juba leitud anagrammi osad.
# Anagrammide leidmine## Leiab mitmeosalisi anagramme# Rekursiivne## sõnade allikaks https://www.eki.ee/tarkvara/wordlist/lemmad2013.txtimport reanagrammi_max_pikkus = 3sona_min_pikkus = 4# teeme sõnalistist dictionary# "räsi" on sõna sorditud kuju: kala => aakl# dictionary 'key' on räsi ja 'value' on kõikide sama räsiga sõnade list[]sonalist = dict()ff = open("lemmad2013.txt", "r", encoding="utf8")for x in ff: sona = x.lower().strip().replace(" ", "") if len(sona)>=sona_min_pikkus: sona_rasi = ''.join(sorted(sona)) if sona_rasi in sonalist: sonalist[sona_rasi].append(sona) else: sonalist[sona_rasi] = [sona]ff.close()print("Saime sõnastikust", str(len(sonalist)), "erineva räsiga sõna.")# defineerime funktsiooni, mis leiab, kas kõik S2 tähed on S1 sees olemas# s1_contains_s2("kalad", "laad") => True#def s1_contains_s2(s1, s2): l1 = len(s1) l2 = len(s2) if(l2>l1): return False for x in s2: s1 = s1.replace(x, '', 1) return (len(s1) == l1 - l2)# defineerime funktsiooni, mis lahutab kõik S2 tähed S1-st# s1_minus_s2("kalad", "kala") => "d"#def s1_minus_s2(s1, s2): for x in s2: s1 = s1.replace(x, '', 1) return(s1)# rekursiivne alam-sõne leidja## sisend:# - ff: sisendfraas, mille anagrammi leitakse. string, tühikuteta# - sonad: list räsidest, mis seal sees võivad veel olla. puhh on eelnevalt välja arvutatud# - seni_leitud: list seni leitud sõnaosadest# - level: kui sügaval me oleme ehk mitmendat anagrammi osa-sõna me leiame# level on piiramiseks, et ei leitaks igavaid lühisõnadest koosnevaid anagrammedef lammuta(ff, sonad, seni_leitud, level): ff = ''.join(sorted(ff)) # viime oma fraasi räsi kujule # kui leiame fraasile listist täpse vaste, siis olemegi leidnud anagrammi # lisame selle anagrammid hulka # duplikaatide eemaldamiseks teeme veidi keemiat: # teisendame leitud anagrammi listiks, sorteerime, teisendame tagasi stringiks if ff in sonad: anagrammid.add(" ".join(sorted((seni_leitud + ' ' + ff).split()))) # rohkematest sõnadest koosnevaid anagramme me leida ei taha, naaseme if level >= anagrammi_max_pikkus: return # järelejäänud fraas on lühike, sellest endast ei saa enam mitmeosalist annagrammi olla, naaseme if len(ff)< sona_min_pikkus*2: return # rekursioon # - võtame kõik fraasis sisalduda võivad räsid # - lahutame nad ükshaaval sisend-fraasist # - rakendame lammuta() funktsiooni jäägi (fraas-räsi) peal for x in sonad: if (len(ff)-len(x)) >= sona_min_pikkus: jaak = s1_minus_s2(ff, x) # nüüd tuleb teha uus list räsidest, mis võivad olla jaak sees sonad_2 = [ k for k in sonad if s1_contains_s2(jaak, k) ] if (len(sonad_2) > 0): lammuta(jaak, sonad_2, seni_leitud + ' ' + x, level+1) # Põhitsükkelwhile(True): # muutujad: # - fraas - fraas, mille anagramme me otsime # - fraas2 - eelmise tühikute ja suurtähtedeta vorm # - sonalist - dictionary kõikidest räsidest ja neile vastavate sõnade listidest # - voimalikud - list räsidest, mis võivad fraas2 sees olla # - anagrammid - algselt tühi list[] leitavatest anagrammidest fraas = input("Kirjuta midagi: ") fraas2 = fraas.lower().strip().replace(' ','') voimalikud = [k for k in sonalist.keys() if s1_contains_s2(fraas2, k)] anagrammid = set() print("Fraas:", fraas) print("Fraasi normkuju:", fraas2) print("Võimalikud anagrammi osad:", len(voimalikud)) print() # Lükkame rekursiooni käima! lammuta(fraas2, voimalikud, "", 1) print ("Leidsime", len(anagrammid), "erinevat anagrammi.") # anagrammid[] on nüüd hulk stringidest # sorteerime need kõigepealt pikkuse järgi, lühemad anagrammid ette - kõibepealt kaheosalised, siis kolmeosalised jne for x in sorted(anagrammid, key=len): # anagrammid on hulk (set), selle element on x on list # tuleb teda veidi kenamaks formaatida ja räsidele sõnastikust vasted otsida ss = "" for y in x.split(): ss = ss + re.sub("[,\']", "", str(sonalist[y])) + " " print(ss) print()