forked from shuboc/LeetCode-2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflip-game-ii.cpp
115 lines (108 loc) · 4.03 KB
/
flip-game-ii.cpp
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// The best theory solution (DP, O(n^2)) could be seen here:
// https://leetcode.com/discuss/64344/theory-matters-from-backtracking-128ms-to-dp-0ms
// Time: O(n + c^3 * 2^c * logc), n is length of string, c is count of "++"
// Space: O(c * 2^c)
// hash solution.
class Solution {
public:
struct multiset_hash {
std::size_t operator() (const multiset<int>& set) const {
string set_string;
for (const auto& i : set) {
set_string.append(to_string(i) + " ");
}
return hash<string>()(set_string);
}
};
bool canWin(string s) {
const int n = s.length();
multiset<int> consecutives;
for (int i = 0; i < n - 1; ++i) { // O(n) time
if (s[i] == '+') {
int c = 1;
for (; i < n - 1 && s[i + 1] == '+'; ++i, ++c);
if (c >= 2) {
consecutives.emplace(c);
}
}
}
return canWinHelper(consecutives);
}
private:
bool canWinHelper(const multiset<int>& consecutives) { // O(2^c) time
if (!lookup_.count(consecutives)) {
bool is_win = false;
for (auto it = consecutives.cbegin(); !is_win && it != consecutives.cend(); ++it) { // O(c) time
const int c = *it;
multiset<int> next_consecutives(consecutives);
next_consecutives.erase(next_consecutives.find(c));
for (int i = 0; !is_win && i < c - 1; ++i) { // O(clogc) time
if (i >= 2) {
next_consecutives.emplace(i);
}
if (c - 2 - i >= 2) {
next_consecutives.emplace(c - 2 - i);
}
is_win = !canWinHelper(next_consecutives);
if (i >= 2) {
next_consecutives.erase(next_consecutives.find(i));
}
if (c - 2 - i >= 2) {
next_consecutives.erase(next_consecutives.find(c - 2 - i));
}
lookup_[consecutives] = is_win; // O(c) time
}
}
}
return lookup_[consecutives];
}
unordered_map<multiset<int>, bool, multiset_hash> lookup_;
};
// Time: O(n + c * n * 2^c), try all the possible game strings,
// and each string would have c choices to become the next string
// Space: O(n * 2^c), keep all the possible game strings
// hash solution.
class Solution2 {
public:
bool canWin(string s) {
if (!lookup_.count(s)) {
const int n = s.length();
bool is_win = false;
for (int i = 0; !is_win && i < n - 1; ++i) {
if (s[i] == '+') {
for (; !is_win && i < n - 1 && s[i + 1] == '+'; ++i) {
s[i] = s[i + 1] = '-';
is_win = !canWin(s);
s[i] = s[i + 1] = '+';
lookup_[s] = is_win;
}
}
}
}
return lookup_[s];
}
private:
unordered_map<string, bool> lookup_;
};
// Time: O(n * c!), n is length of string, c is count of "++"
// Space: O(c), recursion would be called at most c in depth.
// Besides, no extra space in each depth for the modified string.
class Solution3 {
public:
bool canWin(string s) {
const int n = s.length();
bool is_win = false;
for (int i = 0; !is_win && i < n - 1; ++i) { // O(n) time
if (s[i] == '+') {
for (; !is_win && i < n - 1 && s[i + 1] == '+'; ++i) { // O(c) time
s[i] = s[i + 1] = '-';
// t(n, c) = c * t(n, c - 1) + n = ... = c! * t(n, 0) + n * c! * (1/0! + 1/1! + ... 1/c!)
// = n * c! + n * c! * O(e) = O(n * c!)
is_win = !canWin(s);
s[i] = s[i + 1] = '+';
}
}
}
return is_win;
}
};