Skip to content

843. Guess the Word

Problem

Given 100 words of length 6, one of which is the target. You pick one at a time to query how many matches in letters between the one you pick and the target. Matches is calculated as:

"abbccd" and

"aabbcc" has 3 matches at index 0, 2, 4. You need to pick the target within 10 queries.

Solution

A simple solution would be after each query we filter the list of words. For example, the query says the picked one and the target has x matches. We check the matches of the picked one with all the words. Only keep the ones having x matches with the picked word. Next time, we just randomly pick one from the filtered list.

This solution won't pass. A better solution would be not randomly pick. We want the size of the filtered list to be small after a query. Given a word, there are only 7 possible outcomes of the query (0~6 matches). Each of the outcome would result in a different size of the filtered list. We use the maximum among the 7 as the indicator of whether we should pick it. We just compute the indicator of all the words in the list before we pick one with the smallest indicator.

The reason we use the maximum as the indicator is because we want to garantee the worst case since we only have 10 chances.

Code

# """
# This is Master's API interface.
# You should not implement it, or speculate about its implementation
# """
# class Master:
#     def guess(self, word: str) -> int:

import copy


def get_matches(word1, word2):
    ret = 0
    for i in range(len(word1)):
        if word1[i] == word2[i]:
            ret += 1
    return ret


def get_value(index, possible, f):
    count = [0] * 7
    for j in possible:
        if j == index:
            continue
        count[f[index][j]] += 1
    ret = 0
    temp = 0
    max_value = 0
    for i in range(7):
        if count[i] == 0:
            continue
        ret += count[i]
        temp += 1
        max_value = max(max_value, count[i])
    return max_value
    return ret * 1.0 / temp


def pick(possible, f):
    if len(possible) == 1:
        return possible[0]
    ret = -1
    min_value = 1000
    for index in possible:
        value = get_value(index, possible, f)
        if value < min_value:
            ret = index
            min_value = value
    return ret


class Solution:
    def findSecretWord(self, wordlist: List[str], master: 'Master') -> None:
        wordlist = wordlist
        checks = [True] * len(wordlist)
        possible = [i for i in range(len(wordlist))]
        f = [[get_matches(wordlist[i], wordlist[j]) 
              for j in range(len(wordlist))] 
             for i in range(len(wordlist))]

        while len(possible) != 0:
            # print('*')
            index = pick(possible, f)
            word = wordlist[index]
            num_matches = master.guess(word)
            if num_matches == 6:
                return
            possible = [i for i in possible
                        if (get_matches(word, wordlist[i]) == num_matches and
                            word != wordlist[i])]