Zadanie z matury 2018

Zadanie z matury 2018
Dobry Zawodnik
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:14
0

Znajdź słowo, w którym występuje największa liczba różnych liter. Wypisz to słowo i liczbę
występujących w nim różnych liter. Jeśli słów o największej liczbie różnych liter jest więcej
niż jedno, wypisz pierwsze z nich pojawiające się w pliku z danymi.
Zrobilem takie cos i nie wiem co dalej:

Kopiuj
slowo=open("sygnaly.txt")
slownik={}
for znak in slowo:
    slownik[znak]=0
print(len(slownik))
KA
  • Rejestracja:prawie 7 lat
  • Ostatnio:9 dni
  • Lokalizacja:Warszawa
  • Postów:41
0
  1. W pliku są SŁOWA ODDZIELONE ZNAKIEM NOWEJ LINII, a nie jedno słowo :) (tu coś będzie o tym, jak to rozdzielić https://www.pythonforbeginners.com/dictionary/python-split)
  2. Potrzebujesz fora w forze.
Kopiuj
for slowo in slowa:
        znaki_w_slowie = Set()
        for znak in list(slowo):
            znaki_w_slowie.add(znak)

Set zawiera tylko unikalne elementy, więc liczba unikalnych liter w słowie jest równa len(znaki_w_slowie)
Jeszcze kilka poprawek i gotowe :)

EDIT
W zasadzie to można tworzyć Set z list, więc równie dobrze można zrobić znaki_w_slowie = Set(list(slowo)) i drugi for nie jest potrzebny.

edytowany 2x, ostatnio: kamillapinski
grski
@kamillapinski: sam str wystarczy, nie trzeba z niego robić listy, bo str też jest iterowalne :) więc set(slowo) będzie szybsze w tym wypadku
Dobry Zawodnik
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:14
0

czyli do mojego kodu mam dopisać twój czy cały mój mam usunąć ?

grski
  • Rejestracja:ponad 9 lat
  • Ostatnio:9 miesięcy
  • Postów:245
0
Kopiuj
result = {'word': '', 'unique_chars': 0}
with open('xd') as f:
    for line in f:
        word = line.strip().lower()
        unique_chars = len(set(word))
        if result['unique_chars'] < unique_chars: result = {'word': word, 'unique_chars': unique_chars}
print(f"{result['word']} {result['unique_chars']}")

Tłumaczenie tego co robimy:

  1. otwieramy plik f jako context - tak by nie musieć ręcznie robić .close()
  2. jako f otworzony nam został dany plik, oraz przekształcony w iterabla, po którym możemy iterować - iterowanie po nim spowoduje wczytywanie linijka po linijce - to robimy za pomocą pętli for
  3. jako word zapisujemy sobie daną linię (bo jedno słowo jest na linijkę) i usuwamy z niego białe znaki, znaki końca linii itd. metodą strip()
  4. potem tworzymy zmienną unique_chars, która będzie równa długości seta z danego słowa, set() to nic innego jak zbiór unikalnych elemntów z danego iterabla, a stringi w pythonie są iterowalne, więc nie musimy castować na listę czy coś innego
  5. jeśli długość obecnego słowa - unique_chars jest większa od długości zapisanej w rezultacie, to wtedy uaktualniamy rezultat. Dzięki temu, że mamy tu < a nie <= to rezultat zostanie pierwszym jesli póxniej napotkamy inne słowa z taką samą liczbą znaków.
    6, wyprintowujemy wynik

Można całość jeszcze skrócić jeśli nie dbmay o czytelność a tylko o LOC:

Kopiuj
result = {'word': '', 'unique_chars': 0}
with open('xd') as f:
    for line in f:
           if  result['unique_chars'] < len(set(line.strip().lower())): result = {'word': line.strip().lower(), 'unique_chars': len(set(line.strip()))}

Edit - nie wiem, czy A i a są tutaj traktowane jako różne litery, ale jeśli tak, to trzeba wyrzucić .lower()


Napisałem książkę - Programowanie z Górskim: Junior Python Developer
Pora na następny krok na drodze po pierwszą pracę w it i WCALE-NIE-MITYCZNE #programista40k? Zapraszam.
edytowany 3x, ostatnio: grski
enedil
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 23 godziny
  • Postów:1027
2

A dla leniwych (jak akurat lubią tracić mało czasu w trakcie matury), oto szybkie rozwiązanie:

Kopiuj
with open("sygnaly.txt") as f:
    lines = f.read().splitlines()
    m = max(lines, key=lambda s: len(set(s)))
    print(m, len(set(m)))

Edit: wersja używająca mniej dodatkowej pamięci:

Kopiuj
with open("sygnaly.txt") as f:
    m = max(f, key=lambda s: len(set(s)))
    print(m, len(set(m)))
edytowany 5x, ostatnio: enedil
Zobacz pozostałe 2 komentarze
grski
@enedil zgadza się jest liniowe jeśli chodzi o pamięć/czas wykonania, ale w praktyce ładujesz całość pliku do pamięci, przy większym pliku nie jest to zbyt dobre rozwiązanie a no i tego read().splitlines() można by chyba zastąpić readlines()
enedil
@grski: istotnie, z taką zmianą, pamięć będzie maksymalnie liniowa względem najdłuższego słowa.
grski
@enedil nope, .readlines() też ci załaduje do pamięci plik cały :) to jest po prostu krótsza forma zapisu: read().splitlines() jak checsz mieć lazy generator to zamiast f.readlines() wystarczy po prostu... max(f, ...) :)
enedil
Masz rację. Ja natomiast użyłem tej pierwszej metody, gdyż od czasu do czasu przeszkadzał mi pusty element na końcu listy (bo w końcu pliki zwykły kończyć się znakiem końca linii).
Guaz
  • Rejestracja:około 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Częstochowa
  • Postów:221
0

Zaproponuje swoje rozwiązanie jakby ktoś dociekliwy robił test szybkości z jakimś dużym słownikiem :).

Kopiuj
best = ("",0)
with open("sygnaly.txt") as f:
    for line in f:
        tmp_len = len(set(line))
        if tmp_len > best[1]:
            best = (line, tmp_len)
print(*best)

Linux Mint
Arduino / Python 3.5.2
edytowany 1x, ostatnio: Guaz
AF
Kontynuując ciekawostkę. W przypadku dłuższych słowników możemy dokonać dalszej optymalizacji np. dodając linijkę if best[1]>=len(line): continue . Jeżeli nasze słowo nie jest dłuższe od naszego obecnego best to na pewno nie będzie miało więcej unikalnych znaków. Unikamy wywoływania set() dla każdego słowa - dla słownika 3000 angielskich losowych słow zwiększa to wydajność 2x. Albo posortować słowa od największych i przerwać pętlę w momencie kiedy len(line)<=best[1]. Również 2x szybciej. Ale takie ulepszenia to już sztuka dla sztuki w tym momencie :)
Guaz
Fakt, w sumie na to nie wpadłem, a z ciekawości sprawdzałeś może jak wypadają rozwiązania innych?
AF
W sumie to nie ale napisałrem na szybko benchmark, najpierw plik z 100k słowami potem z 10 milionami. Dla mniejszych słowników czasy są identycznie niskie 100K: Grski 93.782 ms Enedil 93.749 ms Guaz 93.752 ms Guaz_skipShorter 46.84 ms SortAndBreakOnShorter 62.163 ms mapToSetAndMax 78.119 ms -> po prostu max(map(set, f), len=key) 10M: Grski 984 ms Enedil 928 ms Guaz 875 ms Guaz_skipShorter 437 ms SortAndBreakOnShorter 593 ms mapToSetAndMax 806 ms Więc wszystkie rozwiązania maja bardzo dobre czasy.
Guaz
Faktycznie, różnica niewielka, myślałem że będą większe różnice ^^. Dzięki za zaspokojenie ciekawości :)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.