diff --git a/300/memo.md b/300/memo.md new file mode 100644 index 0000000..6b673d4 --- /dev/null +++ b/300/memo.md @@ -0,0 +1,49 @@ +# 300. Longest Increasing Subsequence +- sol1: 愚直な解法。素直に解くことができた + - O(n**2) +- O(nlogn)の解法を思いつくことはできない +- https://github.com/mamo3gr/arai60/blob/300_longest-increasing-subsequence/300_longest-increasing-subsequence/memo.md + - この方針だけ読んで考えてたが無理 + - コードを読んでやっと理解 + +bisectの使い方 + +https://docs.python.org/3/library/bisect.html + +```python +from bisect import bisect_left, bisect_right + +a = [1, 3, 5] +print(bisect_left(a, 2)) # 1 +print(bisect_left(a, 3)) # 1 +print(bisect_right(a, 3)) # 2 +``` + +https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0 +> 日本語で長々と書くと、「lengths[i] とは、仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ですよね。 +- 自分のsol1もlength_of_isなのでこの指摘が当てはまる + +https://discord.com/channels/1084280443945353267/1200089668901937312/1209563502407065602 + +https://github.com/mamo3gr/arai60/blob/300_longest-increasing-subsequence/300_longest-increasing-subsequence/memo.md + +- セグ木を使った解法、Binary Indexed Treeを使った解法 + +https://github.com/mamo3gr/arai60/blob/300_longest-increasing-subsequence/300_longest-increasing-subsequence/memo.md +- 色々まとめてくれている + +- セグメントツリー + - https://github.com/naoto-iwase/leetcode/pull/36 +- ほとんど写経で書いてみる +- 1-indexedにすることで、k%2==1: 右の子、k%2==0:左の子となる +- 座標圧縮を行う +- 自分より小さい数字であることを(座標圧縮した)indexで処理し、自分より前の数字であることをnumsの順番のループで処理 +- 自力ではかけない + +- BITを使った解法: LLMに書かせたが自分では読んでいない。(sol4) + - https://algo-logic.info/binary-indexed-tree/ + - 和の区間を最初からに限定することで、メモリと計算効率を上昇 + - bit演算で親や子への移動が可能 + - index & -index = lowbit[index] + - これを足すことで親に移る + - 自分では書けない diff --git a/300/sol1.py b/300/sol1.py new file mode 100644 index 0000000..5358b92 --- /dev/null +++ b/300/sol1.py @@ -0,0 +1,13 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + # Length of increasing subsequence + len_is = [1] * len(nums) + for i, num in enumerate(nums): + for j in range(i): + if num > nums[j]: + len_is[i] = max( + len_is[i], + len_is[j] + 1, + ) + + return max(len_is) diff --git a/300/sol2.py b/300/sol2.py new file mode 100644 index 0000000..98546c4 --- /dev/null +++ b/300/sol2.py @@ -0,0 +1,15 @@ +from bisect import bisect_left + + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + # tails[i] is the smallest ending element of all increasing subsequences of length (i + 1) + tails = [] + for n in nums: + pos_insert_n = bisect_left(tails, n) + if pos_insert_n >= len(tails): + tails.append(n) + continue + tails[pos_insert_n] = n + + return len(tails) diff --git a/300/sol3.py b/300/sol3.py new file mode 100644 index 0000000..d09ac2b --- /dev/null +++ b/300/sol3.py @@ -0,0 +1,56 @@ +from bisect import bisect_left + + +class SegTree: + def __init__(self, size): + n = 1 + while n < size: + n *= 2 + self.offset = n + self.data = [0] * 2 * n + + def _to_leaf_index(self, index): + return index + self.offset - 1 + + def update(self, index, value): + k = self._to_leaf_index(index) + self.data[k] = max(self.data[k], value) + k //= 2 + while k >= 1: + self.data[k] = max(self.data[2 * k], self.data[2 * k + 1]) + k //= 2 + + def query(self, first_index, last_index): + if first_index > last_index: + return 0 + l, r = self._to_leaf_index(first_index), self._to_leaf_index(last_index) + result = 0 + while l <= r: + if l % 2 == 1: + result = max(result, self.data[l]) + l += 1 + if r % 2 == 0: + result = max(result, self.data[r]) + r -= 1 + l //= 2 + r //= 2 + return result + + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + nums_sorted = sorted(set(nums)) + + def num_to_rank(n): + return bisect_left(nums_sorted, n) + 1 + + seg_tree = SegTree(len(nums)) + + max_len = 0 + for n in nums: + rank = num_to_rank(n) + max_len_with_smaller = seg_tree.query(1, rank - 1) + seg_tree.update(rank, max_len_with_smaller + 1) + max_len = max(max_len, max_len_with_smaller + 1) + + return max_len diff --git a/300/sol4.py b/300/sol4.py new file mode 100644 index 0000000..26ac1f4 --- /dev/null +++ b/300/sol4.py @@ -0,0 +1,45 @@ +# Generated by LLM +from bisect import bisect_left +from typing import List + + +class BITMax: + def __init__(self, size: int): + self.n = size + self.bit = [0] * (size + 1) + + def update(self, index: int, value: int) -> None: + while index <= self.n: + if value > self.bit[index]: + self.bit[index] = value + index += index & -index + + def query(self, index: int) -> int: + result = 0 + while index > 0: + if self.bit[index] > result: + result = self.bit[index] + index -= index & -index + return result + + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + if not nums: + return 0 + + coords = sorted(set(nums)) + + def to_rank(x: int) -> int: + return bisect_left(coords, x) + 1 + + bit = BITMax(len(coords)) + ans = 0 + for x in nums: + r = to_rank(x) + best_prev = bit.query(r - 1) + cur = best_prev + 1 + bit.update(r, cur) + if cur > ans: + ans = cur + return ans diff --git a/bisect_test.py b/bisect_test.py new file mode 100644 index 0000000..c14a8a6 --- /dev/null +++ b/bisect_test.py @@ -0,0 +1,6 @@ +from bisect import bisect_left, bisect_right + +a = [1, 3, 5] +print(bisect_left(a, 2)) # 1 +print(bisect_left(a, 3)) # 1 +print(bisect_right(a, 3)) # 2