0843-猜猜这个单词

Raphael Liu Lv10

给你一个由 不同 字符串组成的单词列表 words ,其中 words[i] 长度均为 6words
中的一个单词将被选作秘密单词 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

方法 1:启发式极小化极大算法

想法

显然,可行单词列表中的单词越少越好。如果数据随机,那么我们可以认定这个情况是普遍的。

现在,利用极小化极大算法猜测可行的单词列表。如果我们开始有 N 个单词,我们通过迭代去选择可行单词。

算法

存储 H[i][j]wordlist[i]wordlist[j] 单词匹配数。每次猜测要求之前没有猜过,按照上面的说法实现极小化极大算法,每次选择猜测的单词是当前可行单词中的一个。

[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Solution {
int[][] H;
public void findSecretWord(String[] wordlist, Master master) {
int N = wordlist.length;
H = new int[N][N];
for (int i = 0; i < N; ++i)
for (int j = i; j < N; ++j) {
int match = 0;
for (int k = 0; k < 6; ++k)
if (wordlist[i].charAt(k) == wordlist[j].charAt(k))
match++;
H[i][j] = H[j][i] = match;
}

List<Integer> possible = new ArrayList();
List<Integer> path = new ArrayList();
for (int i = 0; i < N; ++i) possible.add(i);

while (!possible.isEmpty()) {
int guess = solve(possible, path);
int matches = master.guess(wordlist[guess]);
if (matches == wordlist[0].length()) return;
List<Integer> possible2 = new ArrayList();
for (Integer j: possible) if (H[guess][j] == matches) possible2.add(j);
possible = possible2;
path.add(guess);
}

}

public int solve(List<Integer> possible, List<Integer> path) {
if (possible.size() <= 2) return possible.get(0);
List<Integer> ansgrp = possible;
int ansguess = -1;

for (int guess = 0; guess < H.length; ++guess) {
if (!path.contains(guess)) {
ArrayList<Integer>[] groups = new ArrayList[7];
for (int i = 0; i < 7; ++i) groups[i] = new ArrayList<Integer>();
for (Integer j: possible) if (j != guess) {
groups[H[guess][j]].add(j);
}

ArrayList<Integer> maxgroup = groups[0];
for (int i = 0; i < 7; ++i)
if (groups[i].size() > maxgroup.size())
maxgroup = groups[i];

if (maxgroup.size() < ansgrp.size()) {
ansgrp = maxgroup;
ansguess = guess;
}
}
}

return ansguess;
}
}
[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

class Solution(object):
def findSecretWord(self, wordlist, master):
N = len(wordlist)
self.H = [[sum(a==b for a,b in itertools.izip(wordlist[i], wordlist[j]))
for j in xrange(N)] for i in xrange(N)]

possible, path = range(N), ()
while possible:
guess = self.solve(possible, path)
matches = master.guess(wordlist[guess])
if matches == len(wordlist[0]): return
possible = [j for j in possible if self.H[guess][j] == matches]
path = path + (guess,)

def solve(self, possible, path = ()):
if len(possible) <= 2: return possible[0]

ansgrp, ansguess = possible, None
for guess, row in enumerate(self.H):
if guess not in path:
groups = [[] for _ in xrange(7)]
for j in possible:
if j != guess:
groups[row[j]].append(j)
maxgroup = max(groups, key = len)
if len(maxgroup) < len(ansgrp):
ansgrp, ansguess = maxgroup, guess

return ansguess

复杂度分析

  • 时间复杂度:O(N^2 \log{N}),其中 N 是单词的总数,假设其长度为 O(1)。每调用一次 solve 是 O(N),调用次数的上界为 O(\log N)。
  • 空间复杂度:O(N^2)。
 Comments
On this page
0843-猜猜这个单词