-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/276. Paint Fence #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
## Step 1. Initial Solution | ||
|
||
- 初めは制約を気にせずに全ての出現をカウントする方法で実装したがTLEになったので修正 | ||
- O(k^n)なので最大10^1500となり、明らかに不可能 | ||
- 数学的に一つ前の状態からの遷移を考えて以下のように実装 | ||
|
||
```python | ||
class Solution: | ||
def countWays(self, n: int, k: int) -> int: | ||
nonconsecutive = k | ||
consecutive = 0 | ||
for i in range(1, n): | ||
temp = nonconsecutive | ||
nonconsecutive = (k - 1) * (nonconsecutive + consecutive) | ||
consecutive = temp | ||
return consecutive + nonconsecutive | ||
``` | ||
|
||
### Complexity Analysis | ||
|
||
- 時間計算量:O(n) | ||
- 空間計算量:O(1) | ||
|
||
## Step 2. Alternatives | ||
|
||
- nonconsecutive, consecutiveとかではなく、一個前, 更にその前という意味で捉えている人が多そう | ||
- https://github.com/ryoooooory/LeetCode/pull/33/files#diff-5f9ade2f5113d8c983ce290c7d633764f0878ef32ad9d23c1b924860ca38bf43R29 | ||
- 他の選択肢の幅としては上の理解を踏まえるとリストや辞書型にそのインデックスまでの方法を保持しておくものがある | ||
- 再帰的に解く方法もある | ||
- その際、@cacheで2回以上使う出力を保持する方法がある | ||
- 注意点として、countWaysが複数回呼ばれる場合やインスタンスによってキャッシュが使えるかという問題がある | ||
- 基本的には使う関数の上で良いが、複数インスタンス間で共有したい場合はメソッドとして定義せずに外部で関数として定義するのが良いかもしれないという話 | ||
- https://github.com/olsen-blue/Arai60/pull/30/files#r1957966512 | ||
- 行列の累乗で求めることもできるらしい | ||
- 累乗は繰り返し二乗法によりlog nで求められる、とのこと | ||
- 使うかはさておき面白い知識だと思った | ||
- https://github.com/tokuhirat/LeetCode/pull/30/files#r2126104372 | ||
- あとはlru_cacheが話題に上っているので実装してみる | ||
- 基本的にはHashMapでキャッシュした値にアクセスし、最近アクセスしていないものを消せるようにすることが目的 | ||
- https://leetcode.com/problems/lru-cache/ | ||
- を元にしてgetとputが外部から呼び出される前提で実装 | ||
- https://github.com/olsen-blue/Arai60/pull/30/files#diff-50f7b6331a7f8355594f718601d9bd00080e614a343f52dd67abdc08da922ebaR76 | ||
- 凡その流れは分かったのでそのうち復習する | ||
|
||
```python | ||
class Node: | ||
def __init__(self, key: int, val: int): | ||
self.key = key | ||
self.val = val | ||
self.previous = None | ||
self.next = None | ||
|
||
class LRUCache: | ||
def __init__(self, capacity: int): | ||
self.capacity = capacity | ||
self.cache = {} | ||
self.sentinel = Node(-1, -1) | ||
self.sentinel.previous = self.sentinel | ||
self.sentinel.next = self.sentinel | ||
self.size = 0 | ||
|
||
def get(self, key: int) -> int: | ||
if key not in self.cache: | ||
return -1 | ||
node = self.cache[key] | ||
self.remove(key) | ||
self.add(key, node.val) | ||
return node.val | ||
|
||
def put(self, key: int, val: int) -> None: | ||
if key in self.cache: | ||
self.remove(key) | ||
if key not in self.cache and self.size == self.capacity: | ||
self.remove(self.sentinel.previous.key) | ||
self.add(key, val) | ||
|
||
def remove(self, key: int) -> None: | ||
node = self.cache[key] | ||
node.previous.next = node.next | ||
node.next.previous = node.previous | ||
del self.cache[key] | ||
self.size -= 1 | ||
|
||
def add(self, key: int, val:int) -> None: | ||
node = Node(key, val) | ||
self.cache[key] = node | ||
if self.sentinel.previous == self.sentinel: | ||
self.sentinel.previous = node | ||
Comment on lines
+87
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここはLinkedListが空のケース判定だと思いますが、おそらく不要かなと思いました。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sentinel.previousにLRUのnodeを入れている実装なんですが、基本は追加するノード周りの更新作業しかしないので末尾がつながるようにするには、一つ目のnodeを入れる時につないでおく必要があると思っています。 |
||
node.previous = self.sentinel | ||
node.next = self.sentinel.next | ||
node.next.previous = node | ||
self.sentinel.next = node | ||
self.size += 1 | ||
``` | ||
|
||
## Step 3. Final Solution | ||
|
||
- 上も悪くはないが、下のStep1のやり方の方が良さそう | ||
|
||
```python | ||
class Solution: | ||
def countWays(self, n: int, k: int) -> int: | ||
ways_to_index: dict[int, int] = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. バケット探索・衝突解決等考えると、配列の方が若干パフォーマンスが高いかもしれないと思いました。メモリ効率も良さそうです。(そもそも直前とその前の要素のみ保持すればという話もありますが) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. そうなんですね。割と癖でインデックスまでの〇〇を辞書型にしてしまっている節があるので気を付けようと思います。 |
||
ways_to_index[1] = k | ||
ways_to_index[2] = k*k | ||
for i in range(3, n + 1): | ||
ways_to_index[i] = \ | ||
(k - 1) * (ways_to_index[i-1] + ways_to_index[i-2]) | ||
return ways_to_index[n] | ||
``` | ||
|
||
```python | ||
class Solution: | ||
def countWays(self, n: int, k: int) -> int: | ||
end_same = k | ||
end_dif = 0 | ||
for i in range(1, n): | ||
temp = end_same | ||
end_same = (k - 1) * (end_same + end_dif) | ||
end_dif = temp | ||
return end_same + end_dif | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get のときは、既存のキャッシュされたノードが再利用できそうです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
node.valの代わりにnodeを渡した方が良いということですね。
putでvalを入れたかったのでaddの中で作るように統一したんですが、確かに再利用できることを考えるとputでnode作ってnodeを渡す方が良いですね