Last active
February 11, 2021 07:31
-
-
Save classAndrew/6bf07c9330cf8b5912f190f7e0034e06 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome, looks good! I will have to use this when I do my implementation :)