The following is my solution to the killer word problem from Codejam 2011: https://codingcompetitions.withgoogle.com/codejam/round/0000000000433198/0000000000432fe2. In summary, we're playing Hangman. Our opponent intelligently goes through a list of 26 letter in some order. There's a dictionary of valid words, and our goal is find a word that will result in most misses.
The code is correct, i.e. it gets two green checkmarks, but I'm not happy with its readability, so I'd appreciate any feedback. It must work on Ruby 2.5.
def solve(dic, abc)
# Group the words by size and then solve every group.
best_word(dic, abc, abc_pos: -1, groups: dic.group_by(&:size)).first
end
# Iterates over every letter in order and divides the given words into
# groups based on match patterns. Then finds the best word across all
# groups by recursively solving each one.
def best_word(dic, abc, abc_pos:, groups: nil)
word, score = dic.first, 0
return word, score if dic.size < 2 || abc_pos >= abc.size
groups ||= dic.group_by { |word| match_num(word, abc[abc_pos]) }
# Skip the current character if none of the words contains it.
return best_word(dic, abc, abc_pos: abc_pos.succ) if groups[0]&.size == dic.size
groups.each do |num, group|
group_word, group_score = best_word(group, abc, abc_pos: abc_pos.succ)
# Increment the group's score if there were no matches.
group_score += 1 if num.zero?
if group_score > score || (
# Use the word position in the dictionary to break ties.
group_score == score && (dic.index(group_word) < dic.index(word))
)
word = group_word
score = group_score
end
end
return word, score
end
def match_num(word, chr)
num = 0
offset = 0
radix = word.size + 1
while i = word.index(chr, offset)
offset = i + 1
num = num * radix + offset
end
num
end
def main
t = gets.to_i
t.times.each do |c|
n, m = gets.split.map(&:to_i)
dic = Array.new(n) { gets.rstrip }
abcs = Array.new(m) { gets.rstrip }
ans = abcs.map { |abc| solve(dic, abc) }.join(" ")
puts "Case ##{c+1}: #{ans}"
end
end
if __FILE__ == $PROGRAM_NAME
main
end
Example output:
$ cat tests.txt
2
3 2
banana
caravan
pajamas
abcdefghijklmnopqrstuvwxyz
etaoisnhrdlcumwfgypbvkjxqz
4 1
potato
tomato
garlic
pepper
zyxwvutsrqponmlkjihgfedcba
$ ruby solution.rb < tests.txt
Case #1: pajamas caravan
Case #2: garlic