From 76dd68a31f5ca52668d6e4d18bd49cbcde51c338 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Fri, 27 Mar 2026 08:53:28 +0900 Subject: [PATCH 1/4] Add first step --- 300/memo.md | 44 +++++++++++++++++++++++++++++++++++++++ 300/sol1.py | 9 ++++++++ 300/sol2.py | 14 +++++++++++++ 300/sol3.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 300/sol4.py | 44 +++++++++++++++++++++++++++++++++++++++ bisect_test.py | 6 ++++++ 6 files changed, 173 insertions(+) create mode 100644 300/memo.md create mode 100644 300/sol1.py create mode 100644 300/sol2.py create mode 100644 300/sol3.py create mode 100644 300/sol4.py create mode 100644 bisect_test.py diff --git a/300/memo.md b/300/memo.md new file mode 100644 index 0000000..a3fe727 --- /dev/null +++ b/300/memo.md @@ -0,0 +1,44 @@ +# 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) + - この復習から次回 diff --git a/300/sol1.py b/300/sol1.py new file mode 100644 index 0000000..b4e6ec9 --- /dev/null +++ b/300/sol1.py @@ -0,0 +1,9 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + length_of_is = [1] * len(nums) + for i, n in enumerate(nums): + for j in range(i): + if n > nums[j]: + length_of_is[i] = max(length_of_is[i], length_of_is[j] + 1) + + return max(length_of_is) diff --git a/300/sol2.py b/300/sol2.py new file mode 100644 index 0000000..be05e31 --- /dev/null +++ b/300/sol2.py @@ -0,0 +1,14 @@ +from bisect import bisect_left + + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + tails = [] + for n in nums: + lis_if_tail_is_n = bisect_left(tails, n) + 1 + if lis_if_tail_is_n > len(tails): + tails.append(n) + continue + tails[lis_if_tail_is_n - 1] = n + + return len(tails) diff --git a/300/sol3.py b/300/sol3.py new file mode 100644 index 0000000..a50e809 --- /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, rank, value): + k = self._to_leaf_index(rank) + 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_rank, last_rank): + if first_rank > last_rank: + return 0 + l, r = self._to_leaf_index(first_rank), self._to_leaf_index(last_rank) + 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..8d2aa71 --- /dev/null +++ b/300/sol4.py @@ -0,0 +1,44 @@ +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 From 247c00fa7eb47aaaa1a2b8a1088977c38fba2655 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Fri, 27 Mar 2026 12:03:16 +0900 Subject: [PATCH 2/4] Add BIT --- 300/memo.md | 7 ++++++- 300/sol4.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/300/memo.md b/300/memo.md index a3fe727..6b673d4 100644 --- a/300/memo.md +++ b/300/memo.md @@ -41,4 +41,9 @@ https://github.com/mamo3gr/arai60/blob/300_longest-increasing-subsequence/300_lo - 自力ではかけない - BITを使った解法: LLMに書かせたが自分では読んでいない。(sol4) - - この復習から次回 + - https://algo-logic.info/binary-indexed-tree/ + - 和の区間を最初からに限定することで、メモリと計算効率を上昇 + - bit演算で親や子への移動が可能 + - index & -index = lowbit[index] + - これを足すことで親に移る + - 自分では書けない diff --git a/300/sol4.py b/300/sol4.py index 8d2aa71..26ac1f4 100644 --- a/300/sol4.py +++ b/300/sol4.py @@ -1,3 +1,4 @@ +# Generated by LLM from bisect import bisect_left from typing import List From 7c0dc0cb8ca251104b1add90497c3a5f73a2fd90 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sat, 28 Mar 2026 05:38:17 +0900 Subject: [PATCH 3/4] Add suggested changes --- 300/sol1.py | 4 ++-- 300/sol2.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/300/sol1.py b/300/sol1.py index b4e6ec9..b3e838d 100644 --- a/300/sol1.py +++ b/300/sol1.py @@ -1,9 +1,9 @@ class Solution: def lengthOfLIS(self, nums: List[int]) -> int: length_of_is = [1] * len(nums) - for i, n in enumerate(nums): + for i, num in enumerate(nums): for j in range(i): - if n > nums[j]: + if num > nums[j]: length_of_is[i] = max(length_of_is[i], length_of_is[j] + 1) return max(length_of_is) diff --git a/300/sol2.py b/300/sol2.py index be05e31..6427ee4 100644 --- a/300/sol2.py +++ b/300/sol2.py @@ -5,10 +5,10 @@ class Solution: def lengthOfLIS(self, nums: List[int]) -> int: tails = [] for n in nums: - lis_if_tail_is_n = bisect_left(tails, n) + 1 - if lis_if_tail_is_n > len(tails): + pos_insert_n = bisect_left(tails, n) + if pos_insert_n >= len(tails): tails.append(n) continue - tails[lis_if_tail_is_n - 1] = n + tails[pos_insert_n] = n return len(tails) From d2bf9624cd397f69dc5b75b3b6b9d10d8f745dd6 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sun, 29 Mar 2026 05:46:52 +0900 Subject: [PATCH 4/4] Apply suggested changes --- 300/sol1.py | 10 +++++++--- 300/sol2.py | 1 + 300/sol3.py | 10 +++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/300/sol1.py b/300/sol1.py index b3e838d..5358b92 100644 --- a/300/sol1.py +++ b/300/sol1.py @@ -1,9 +1,13 @@ class Solution: def lengthOfLIS(self, nums: List[int]) -> int: - length_of_is = [1] * len(nums) + # Length of increasing subsequence + len_is = [1] * len(nums) for i, num in enumerate(nums): for j in range(i): if num > nums[j]: - length_of_is[i] = max(length_of_is[i], length_of_is[j] + 1) + len_is[i] = max( + len_is[i], + len_is[j] + 1, + ) - return max(length_of_is) + return max(len_is) diff --git a/300/sol2.py b/300/sol2.py index 6427ee4..98546c4 100644 --- a/300/sol2.py +++ b/300/sol2.py @@ -3,6 +3,7 @@ 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) diff --git a/300/sol3.py b/300/sol3.py index a50e809..d09ac2b 100644 --- a/300/sol3.py +++ b/300/sol3.py @@ -12,18 +12,18 @@ def __init__(self, size): def _to_leaf_index(self, index): return index + self.offset - 1 - def update(self, rank, value): - k = self._to_leaf_index(rank) + 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_rank, last_rank): - if first_rank > last_rank: + def query(self, first_index, last_index): + if first_index > last_index: return 0 - l, r = self._to_leaf_index(first_rank), self._to_leaf_index(last_rank) + l, r = self._to_leaf_index(first_index), self._to_leaf_index(last_index) result = 0 while l <= r: if l % 2 == 1: