Skip to content

Commit 06e887d

Browse files
committed
LC 1964. Find the Longest Valid Obstacle Course at Each Position (Python)
1 parent acca451 commit 06e887d

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ to the solution in this repository.
637637
| [1945. Sum of Digits of String After Convert][lc1945] | 🟢 Easy | [![rust](res/rs.png)][lc1945rs] |
638638
| [1962. Remove Stones to Minimize the Total][lc1962] | 🟠 Medium | [![python](res/py.png)][lc1962py] |
639639
| [1963. Minimum Number of Swaps to Make the String Balanced][lc1963] | 🟠 Medium | [![rust](res/rs.png)][lc1963rs] |
640-
| [1964. Find the Longest Valid Obstacle Course at Each Position][lc1964] | 🔴 Hard | [![rust](res/rs.png)][lc1964rs] |
640+
| [1964. Find the Longest Valid Obstacle Course at Each Position][lc1964] | 🔴 Hard | [![python](res/py.png)][lc1964py] [![rust](res/rs.png)][lc1964rs] |
641641
| [1970. Last Day Where You Can Still Cross][lc1970] | 🔴 Hard | [![rust](res/rs.png)][lc1970rs] |
642642
| [1971. Find if Path Exists in Graph][lc1971] | 🟢 Easy | [![python](res/py.png)][lc1971py] |
643643
| [1980. Find Unique Binary String][lc1980] | 🟠 Medium | [![rust](res/rs.png)][lc1980rs] |
@@ -2178,6 +2178,7 @@ to the solution in this repository.
21782178
[lc1963]: https://leetcode.com/problems/minimum-number-of-swaps-to-make-the-string-balanced/
21792179
[lc1963rs]: leetcode/minimum-number-of-swaps-to-make-the-string-balanced.rs
21802180
[lc1964]: https://leetcode.com/problems/find-the-longest-valid-obstacle-course-at-each-position/
2181+
[lc1964py]: leetcode/find-the-longest-valid-obstacle-course-at-each-position.py
21812182
[lc1964rs]: leetcode/find-the-longest-valid-obstacle-course-at-each-position.rs
21822183
[lc1970]: https://leetcode.com/problems/last-day-where-you-can-still-cross/
21832184
[lc1970rs]: leetcode/last-day-where-you-can-still-cross.rs
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# 1964. Find the Longest Valid Obstacle Course at Each Position
2+
# 🔴 Hard
3+
#
4+
# https://leetcode.com/problems/find-the-longest-valid-obstacle-course-at-each-position/
5+
#
6+
# Tags: Array - Binary Search - Divide and Conquer - Binary Indexed Tree
7+
# - Segment Tree - Merge Sort - Ordered Set
8+
9+
import timeit
10+
from bisect import bisect_right
11+
from typing import List
12+
13+
14+
# Use dynamic programming, each subproblem can be seen as "the longest
15+
# non-decreasing subsequence" but, if we use something similar to the
16+
# LIS solution, it will run in O(n^2*log(n)), we can instead see that,
17+
# for each index, we only need to find the longest sequence to the left
18+
# where the last element is less than, or equal to the current element,
19+
# because then we can append the current element. We could do this in
20+
# O(n), resulting in O(n^2) overall time complexity, iterating over all
21+
# the previous results but that can be optimized if we use an extra
22+
# structure where we keep these previous results sorted and can binary
23+
# search the insertion point.
24+
#
25+
# Time complexity: O(n*log(n)) - We iterate over all the elements, for
26+
# each, we do a binary search over the previous results that could be up
27+
# to n.
28+
# Space complexity: O(n) - The dp array uses n extra memory.
29+
#
30+
# Runtime 1025 ms Beats 31%
31+
# Memory 34.65 MB Beats 37%
32+
class Solution:
33+
def longestObstacleCourseAtEachPosition(
34+
self, obstacles: List[int]
35+
) -> List[int]:
36+
# dp[i] holds the smallest value that we have seen as the last
37+
# element of a sequence of length i. It is guaranteed to be
38+
# non-decreasing.
39+
res, dp = [], [float("inf")] * (len(obstacles) + 1)
40+
for o in obstacles:
41+
idx = bisect_right(dp, o)
42+
res.append(idx + 1)
43+
dp[idx] = o
44+
return res
45+
46+
47+
def test():
48+
executors = [Solution]
49+
tests = [
50+
[[8], [1]],
51+
[[8, 1], [1, 1]],
52+
[[2, 2, 1], [1, 2, 1]],
53+
[[8, 1, 8], [1, 1, 2]],
54+
[[1, 2, 3, 2], [1, 2, 3, 3]],
55+
[[3, 1, 5, 6, 4, 2], [1, 1, 2, 3, 2, 2]],
56+
[[5, 1, 5, 5, 1, 3, 4, 5, 1, 4], [1, 1, 2, 3, 2, 3, 4, 5, 3, 5]],
57+
]
58+
for executor in executors:
59+
start = timeit.default_timer()
60+
for _ in range(1):
61+
for col, t in enumerate(tests):
62+
sol = executor()
63+
result = sol.longestObstacleCourseAtEachPosition(t[0])
64+
exp = t[1]
65+
assert result == exp, (
66+
f"\033[93m» {result} <> {exp}\033[91m for"
67+
+ f" test {col} using \033[1m{executor.__name__}"
68+
)
69+
stop = timeit.default_timer()
70+
used = str(round(stop - start, 5))
71+
cols = "{0:20}{1:10}{2:10}"
72+
res = cols.format(executor.__name__, used, "seconds")
73+
print(f"\033[92m» {res}\033[0m")
74+
75+
76+
test()

0 commit comments

Comments
 (0)