1871. Jump Game VII#68
Conversation
| @@ -1 +1,91 @@ | |||
| # Step1 | |||
|
|
|||
| ## Code1-1 | |||
There was a problem hiding this comment.
自分でも解いてみまし。シンプルな解法が思いつかず、 range query を平方分割で実装して解きました。初めは Binary Indexed Tree を二次元配列で書き始めたのですが、更新部分が上手く書けず、平方分割に逃げました。
供養のために貼っておきます。
class Solution {
public:
bool canReach(string s, int minJump, int maxJump) {
if (s[s.size() - 1] != '0') {
return false;
}
vector<int8_t> reached(BASE * BASE);
vector<int> reached_sqrt_count(BASE);
set(reached, reached_sqrt_count, 0, 1);
for (int i = 0; i < s.size(); ++i) {
if (s[i] != '0') {
continue;
}
if (!get(reached, reached_sqrt_count, i)) {
continue;
}
set(reached, reached_sqrt_count, i + minJump, min(i + maxJump, (int)s.size() - 1) + 1);
}
return get(reached, reached_sqrt_count, s.size() - 1);
}
void set(vector<int8_t>& reached, vector<int>& reached_sqrt_count, int begin, int end) {
int left = (begin + BASE - 1) / BASE * BASE;
int right = end / BASE * BASE;
for (int bucket = left / BASE; bucket < right / BASE; ++bucket) {
reached_sqrt_count[bucket] = BASE;
}
if (begin / BASE == end / BASE) {
for (int i = begin; i < end; ++i) {
set(reached, reached_sqrt_count, i);
}
} else {
for (int i = begin; i < left; ++i) {
set(reached, reached_sqrt_count, i);
}
for (int i = right; i < end; ++i) {
set(reached, reached_sqrt_count, i);
}
}
}
void set(vector<int8_t>& reached, vector<int>& reached_sqrt_count, int i) {
if (reached[i]) {
return;
}
reached[i] = true;
++reached_sqrt_count[i / BASE];
}
bool get(const vector<int8_t>& reached, const vector<int>& reached_sqrt_count, int index) {
return reached_sqrt_count[index / BASE] == BASE ||
reached[index];
}
private:
static constexpr const int BASE = 1000;
};There was a problem hiding this comment.
reached_sqrt_count[i]はバケットiのうち到達可能な個数を表しているのですか?
for (int bucket = left / BASE; bucket < right / BASE; ++bucket) {
reached_sqrt_count[bucket] = BASE;
}この部分の更新は, バケット内すべてを到達可能とみなしているように思いましたが、バケット内のインデックスjでs[j]=1なるjに対しても, 到達可能としていることになりませんか?
There was a problem hiding this comment.
その理解であっているように思います。
本来やりたいことは reached の [begin, end) の範囲を true にすることです。一方、これには O(N) かかります。これを避けるため、 reached を sqrt(reached.size()) 個のバケットに分け、 reached の [left, right) の範囲を true とする代わりに、対応する reached_sqrt_count を BASE に設定しています。これによりO(sqrt(N)) になります。
また、 get() する際に、 reached_sqrt_count が BASE となっている、つまりバケット内のすべての reached が true となっているか、 reached[index] のどちらがが true となっていれば、 true を返すようにしています。
ただ、 set() が間違っていることに気づきました。正しくは以下の通りです。
void set(vector<int8_t>& reached, vector<int>& reached_sqrt_count, int i) {
if (get(reached, reached_sqrt_count, i)) {
return;
}
reached[i] = true;
++reached_sqrt_count[i / BASE];
}元のコードでも Accepted してしまっていました。 LeetCode のテストケースが弱かったためだと思います。
なお、平方分割はソフトウェアエンジニアの常識には含まれていないと思います。
There was a problem hiding this comment.
理解できました。平方分割は自分もよくわからなくて調べながら理解したのですが、解法として浮かんでいなかったので勉強になりました。
https://leetcode.com/problems/jump-game-vii/description/