843. 猜猜这个单词
难度困难123
给你一个由 不同 字符串组成的单词列表 words ,其中 words[i] 长度均为 6 。words 中的一个单词将被选作秘密单词 secret 。
另给你一个辅助对象 Master ,你可以调用 Master.guess(word) 来猜单词,其中参数 word 长度为 6 且必须是 words 中的字符串。
Master.guess(word) 将会返回如下结果:
如果 word 不是 words 中的字符串,返回 -1 ,或者
一个整数,表示你所猜测的单词 word 与 秘密单词 secret 的准确匹配(值和位置同时匹配)的数目。
每组测试用例都会包含一个参数 allowedGuesses ,其中 allowedGuesses 是你可以调用 Master.guess(word) 的最大次数。
对于每组测试用例,在不超过允许猜测的次数的前提下,你应该调用 Master.guess 来猜出秘密单词。最终,你将会得到以下结果:
如果你调用 Master.guess 的次数大于 allowedGuesses 所限定的次数或者你没有用 Master.guess 猜到秘密单词,则得到 "Either you took too many guesses, or you did not find the secret word." 。
如果你调用 Master.guess 猜到秘密单词,且调用 Master.guess 的次数小于或等于 allowedGuesses ,则得到 "You guessed the secret word correctly." 。
生成的测试用例保证你可以利用某种合理的策略(而不是暴力)猜到秘密单词。
示例 1:
输入:secret = "acckzz", words = ["acckzz","ccbazz","eiowzz","abcczz"], allowedGuesses = 10
输出:You guessed the secret word correctly.
解释:
master.guess("aaaaaa") 返回 -1 ,因为 "aaaaaa" 不在 words 中。
master.guess("acckzz") 返回 6 ,因为 "acckzz" 是秘密单词 secret ,共有 6 个字母匹配。
master.guess("ccbazz") 返回 3 ,因为 "ccbazz" 共有 3 个字母匹配。
master.guess("eiowzz") 返回 2 ,因为 "eiowzz" 共有 2 个字母匹配。
master.guess("abcczz") 返回 4 ,因为 "abcczz" 共有 4 个字母匹配。
一共调用 5 次 master.guess ,其中一个为秘密单词,所以通过测试用例。
示例 2:
输入:secret = "hamada", words = ["hamada","khaled"], allowedGuesses = 10
输出:You guessed the secret word correctly.
解释:共有 2 个单词,且其中一个为秘密单词,可以通过测试用例。
提示:
1 <= words.length <= 100
words[i].length == 6
words[i] 仅由小写英文字母组成
words 中所有字符串 互不相同
secret 存在于 words 中
10 <= allowedGuesses <= 30
#解题思路
一开始看到这个题目,我连题目想要做什么都没看懂。后面看了youtube视频才知道,题目想要我们设计一个算法, 让我们能够尽可能把调用master.guess api的次数控制在 10次以内。主要解题思路如下:
secret = "acckzz", words = ["acckzz","ccbazz","eiowzz","abcczz"], allowedGuesses = 10
先从words里面随机选择一个单词:使用random产生一个在[0,len(words)-1] 之间的index,选出这个单词,例如: abcczz
调用guess API,返回一个当前匹配的次数。master.guess("abcczz") 函数会返回 4
创建一个candidates数组;以 abcczz 为基准,遍历words里面的其他单词,寻找匹配值 >= 4 的单词,加入到candidates
第一次遍历之后,candidates数组长这样:["acckzz"]
让candidates数组等于words,重新随机选取一个单词,重复1,2,3,4,5的步骤,直到candidates数组为空,或者调用guess API的返回值 == 6 (len(secret)
#原创代码
class Solution(object):
import random
def findSecretWord(self, words, master):
# 1.随便从word list里面挑选一个单词 w
# 2. 呼叫guess word,看这个单词和 secret word有几个匹配的,例如返回2
# 3. 用这个单词 w 和 word list里面所有的单词进行匹配,如果发现 >=2 个匹配的,放进candidate list
# 4. 再随便从candidate list里面挑选一个单词,和secret word进行匹配,重复上述步骤
n = len(words)
if n == 0: return
index = random.randint(0,n-1)
first_guess = words[index]
matching_num = master.guess(first_guess)
if matching_num == len(words): return
candidates = []
def compareWord(word1, word2): # compare word 部分的逻辑代码可以进一步优化
i,j = 0,0
count = 0
while i < len(word1) and j < len(word2):
if word1[i] == word2[j]:
count += 1
i += 1
j += 1
return count
for word in words:
if word == first_guess:
continue
if word != first_guess:
if compareWord(word, first_guess) >= matching_num:
candidates.append(word)
self.findSecretWord(candidates, master) # 递归调用的写法可以优化
# 报错:
# empty range for randrange() (0,0, 0) and ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width)) -- 因为candidates在最后的时候会变为空,所以在新一轮调用之前,要对candidates的状态进行判断,如果数组已经是空,说明已经找到了这个单词,函数直接return即可
#优化代码:
不一定得递归调用,可以通过while循环,让words = candidates 数组的方式不断更新words数组
compareword的写法:不一定使用双指针,可以使用python里面的zip方法,一个一个单词去比较
class Solution:
import random
def findSecretWord(self, words, master):
times = 0
match_num = 0
while times < 10 and match_num != 6:
n = len(words)
index = random.randint(0,n-1)
guess_word = words[index]
match_num = master.guess(guess_word)
candidates = []
for word in words:
if word != guess_word:
if self.compareWord(guess_word, word) == match_num:
candidates.append(word)
words = candidates
def compareWord(self,word1, word2): # rtype: int
count = 0
for x,y in zip(word1,word2):
if x == y:
count += 1
return count
#时空复杂度:
# Time: O(10*N) go through each word
# Space: O(10*N) - candidates array
# 时间复杂度:O(10n) = O(n),因为 for 循环运行 10 次或更少,并且在每次迭代中,我们遍历词表,
# 空间复杂度:O(10n) = O(n)