Skip to content

Commit 1aacdb0

Browse files
committed
0843-guess-the-word
1 parent ea22e39 commit 1aacdb0

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/// 843. Guess the Word
2+
/// We are given a word list of unique words, each word is 6 letters long, and one word in this
3+
/// list is chosen as secret. You may call master.guess(word) to guess a word. The guessed word
4+
/// should have type string and must be from the original list with 6 lowercase letters. This
5+
/// function returns an integer type, representing the number of exact matches (value and position)
6+
/// of your guess to the secret word. Also, if your guess is not in the given wordlist, it will
7+
/// return -1 instead.
8+
/// For each test case, you have 10 guesses to guess the word. At the end of any number of calls,
9+
/// if you have made 10 or less calls to master.guess and at least one of these guesses was the
10+
/// secret, you pass the testcase.
11+
/// Besides the example test case below, there will be 5 additional test cases, each with 100 words
12+
/// in the word list. The letters of each word in those testcases were chosen independently at
13+
/// random from 'a' to 'z', such that every word in the given word lists is unique.
14+
///
15+
/// Example 1:
16+
/// Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"]
17+
///
18+
/// Explanation:
19+
///
20+
/// master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist.
21+
/// master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 matches.
22+
/// master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches.
23+
/// master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches.
24+
/// master.guess("abcczz") returns 4, because "abcczz" has 4 matches.
25+
///
26+
/// We made 5 calls to master.guess and one of them was the secret, so we pass the test case.
27+
28+
import XCTest
29+
30+
final class Master {
31+
private(set) var guessCount = 0
32+
private(set) var secretFound = false
33+
34+
private var wordlist: Set<String>
35+
private var secret: [Character]
36+
37+
init(wordlist: [String], secret: String) {
38+
self.wordlist = Set(wordlist)
39+
self.secret = Array(secret)
40+
}
41+
42+
func guess(word: String) -> Int {
43+
guessCount += 1
44+
guard wordlist.contains(word) else { return -1 }
45+
let word = Array(word)
46+
var match = 0
47+
for i in 0..<6 {
48+
if secret[i] == word[i] {
49+
match += 1
50+
}
51+
}
52+
if match == 6 {
53+
secretFound = true
54+
}
55+
return match
56+
}
57+
}
58+
59+
/// Approach: Minimax
60+
func findSecretWord(_ wordlist: [String], _ master: Master) {
61+
let n = wordlist.count
62+
var h = Array(repeating: Array(repeating: -1, count: n), count: n)
63+
for i in 0..<n {
64+
for j in 0..<n {
65+
guard h[i][j] == -1 else { continue }
66+
var wordI = Array(wordlist[i])
67+
var wordJ = Array(wordlist[j])
68+
var match = 0
69+
for k in 0..<6 {
70+
if wordI[k] == wordJ[k] {
71+
match += 1
72+
}
73+
}
74+
h[i][j] = match
75+
h[j][i] = match
76+
}
77+
}
78+
79+
func nextGuess(possible: [Int]) -> Int {
80+
guard possible.count > 2 else { return possible[0] }
81+
var minSize = possible.count
82+
var minGuess = 0
83+
for guess in possible {
84+
var groups = Array(repeating: 0, count: 7)
85+
for p in possible {
86+
if p != guess {
87+
groups[h[guess][p]] += 1
88+
}
89+
}
90+
if let size = groups.max(),
91+
size < minSize {
92+
minSize = size
93+
minGuess = guess
94+
}
95+
}
96+
return minGuess
97+
}
98+
99+
var possible = Array(0..<n)
100+
while !possible.isEmpty {
101+
let guess = nextGuess(possible: possible)
102+
let match = master.guess(word: wordlist[guess])
103+
guard match < 6 else { return }
104+
var temp: [Int] = []
105+
for p in possible {
106+
if h[guess][p] == match {
107+
temp.append(p)
108+
}
109+
}
110+
possible = temp
111+
}
112+
}
113+
114+
class Tests: XCTestCase {
115+
func testExample() {
116+
let wordlist = ["acckzz", "ccbazz", "eiowzz", "abcczz"]
117+
let secret = "acckzz"
118+
let master = Master(wordlist: wordlist, secret: secret)
119+
findSecretWord(wordlist, master)
120+
XCTAssertTrue(master.secretFound)
121+
XCTAssertTrue(master.guessCount <= 10)
122+
}
123+
}
124+
125+
Tests.defaultTestSuite.run()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='macos' executeOnSourceChanges='false'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ You can visit the pages below to search problems by company tags. Then come back
5555
482 | [License Key Formatting](https://leetcode.com/problems/license-key-formatting/description/) | [Solution](https://github.com/zhubofei/LeetCode-Swift/blob/master/0482-license-key-formatting.playground/Contents.swift) | String
5656
681 | [Next Closest Time](https://leetcode.com/problems/next-closest-time/description/) | [Solution](https://github.com/zhubofei/LeetCode-Swift/blob/master/0681-next-closest-time.playground/Contents.swift) | Simulation
5757
683 | [K Empty Slots](https://leetcode.com/problems/k-empty-slots/description/) | [Solution](https://github.com/zhubofei/LeetCode-Swift/blob/master/0683-k-empty-slots.playground/Contents.swift) | Sliding Window
58+
843 | [Guess the Word \*](https://leetcode.com/problems/guess-the-word/description/) | [Solution](https://github.com/zhubofei/LeetCode-Swift/blob/master/0843-guess-the-word.playground/Contents.swift) | Minimax
59+
60+
**\* problems are not compilable on LeetCode. I have filed bug report for these problems**

0 commit comments

Comments
 (0)