Skip to content

Instantly share code, notes, and snippets.

@classAndrew
Last active February 11, 2021 07:31
Show Gist options
  • Save classAndrew/6bf07c9330cf8b5912f190f7e0034e06 to your computer and use it in GitHub Desktop.
Save classAndrew/6bf07c9330cf8b5912f190f7e0034e06 to your computer and use it in GitHub Desktop.
from collections import Counter
# for K, M-length dictionary words, and an N-tile hand, you can expect a complexity of
# O(KM * lg M) Assuming letter counting and comparing is done in O(n) time (it isn't but
# it can be with the cost of lots of memory) There's a pretty large coefficient of 2^26 since
# there's 26 characters.
# tiles our hand!
hand = "buncheesefoodragon"
# you can realize right away that there's "bun", "cheese", "food", "dragon", "on"
# this creates the mapping. if our hand was "e,k,p,e" then it will make sure that all words
# with the letters e, k, p will be grouped together in a map entry
# e.g. map["ekp"] = ["keep", "peek, "kepe"]
mapping = {}
with open('corncob_lowercase.txt') as f:
for word in f:
# get rid of the newline. Some dictionaries use mixed cases
word = word[:-1].lower()
k = ''.join(sorted(set(word)))
if not k in mapping:
mapping[k] = []
mapping[k].append(word)
# counter is just the mapping of each character to count in string. trivial to implement
# you'll see why this helps later
hand_count = Counter(hand)
# get this to be indexable
# sorting ensures that we stay in the same order that mapping's keys follow
unique = sorted(list(hand_count))
# this is to determine our subset length
uniqlen = len(unique)
# our result
can_make = []
# generate subsets, I will cheese this with bitsets. Extremely underrated trick
for i in range(1 << uniqlen):
# generate the key for mapping we check each bit by index
subs = ''.join(unique[j] for j in range(uniqlen) if i & (1 << j))
# key not found, skip it :)
if subs not in mapping:
continue
for potential_word in mapping[subs]:
# we check if we have enough tiles for the word
# slightly inefficient- a better alternative would be to store counts for all words inside mapping
# but that means blowing up memory for many entries we won't even visit
if all(potential_word.count(c) <= hand_count[c] for c in potential_word):
can_make.append(potential_word)
print(can_make)
@TorNATO-PRO
Copy link

Awesome, looks good! I will have to use this when I do my implementation :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment