Skip to content

Commit e7149e4

Browse files
committed
LeetCode 2389. Longest Subsequence With Limited Sum
1 parent 3400dc1 commit e7149e4

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
366366
| [2136. Earliest Possible Day of Full Bloom][lc2136] | 🔴 Hard | [![python](res/py.png)][lc2136py] |
367367
| [2225. Find Players With Zero or One Losses][lc2225] | 🟢 Easy | [![python](res/py.png)][lc2225py] |
368368
| [2256. Minimum Average Difference][lc2256] | 🟠 Medium | [![python](res/py.png)][lc2256py] |
369+
| [2389. Longest Subsequence With Limited Sum][lc2389] | 🟢 Easy | [![python](res/py.png)][lc2389py] |
369370
| [2481. Minimum Cuts to Divide a Circle][lc2481] | 🟢 Easy | [![python](res/py.png)][lc2481py] |
370371

371372
[🔝 Back to Top 🔝](#coding-challenges)
@@ -1073,6 +1074,8 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
10731074
[lc2225py]: leetcode/find-players-with-zero-or-one-losses.py
10741075
[lc2256]: https://leetcode.com/problems/minimum-average-difference/
10751076
[lc2256py]: leetcode/minimum-average-difference.py
1077+
[lc2389]: https://leetcode.com/problems/longest-subsequence-with-limited-sum/
1078+
[lc2389py]: leetcode/longest-subsequence-with-limited-sum.py
10761079
[lc2481]: https://leetcode.com/problems/minimum-cuts-to-divide-a-circle/
10771080
[lc2481py]: leetcode/minimum-cuts-to-divide-a-circle.py
10781081

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# 2389. Longest Subsequence With Limited Sum
2+
# 🟢 Easy
3+
#
4+
# https://leetcode.com/problems/longest-subsequence-with-limited-sum/
5+
#
6+
# Tags: Array - Binary Search - Greedy - Sorting - Prefix Sum
7+
8+
import timeit
9+
from bisect import bisect_right
10+
from itertools import accumulate
11+
from typing import List
12+
13+
14+
# Create an array of prefix sums of the sorted input nums, result[i]
15+
# will be the index at which we would need to insert that value on the
16+
# prefix sum array to maintain the sorted order. What the algorithm does
17+
# is to greedily pick the highest number of values that gives the lowest
18+
# possible subsequence sum, then use binary search to determine how
19+
# many values we can take while remaining below the given sum value.
20+
#
21+
# Time complexity: O(n*log(n) + k*log(n)) - Where n is the number of
22+
# elements in the nums array and k is the number of queries, we need to
23+
# sort the array nums in n*log(n) time, and compute the prefix sums in
24+
# O(n), then, for each query k, we use binary search in O(log(n)) time
25+
# to find the minimum number of values we can sum while still remaining
26+
# below the query value.
27+
# Space complexity: O(n) - The prefix sum array has the same size as the
28+
# nums array.
29+
#
30+
# Runtime 99 ms Beats 98.35%
31+
# Memory 14.1 MB Beats 79.61%
32+
class Pythonic:
33+
def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
34+
prefix_sums = [ps for ps in accumulate(sorted(nums))]
35+
return [bisect_right(prefix_sums, query) for query in queries]
36+
37+
38+
# Same logic as the previous solution but avoiding the use of any
39+
# libraries.
40+
#
41+
# Time complexity: O(n*log(n) + k*log(n)).
42+
# Space complexity: O(n).
43+
#
44+
# Runtime 118 ms Beats 90.49%
45+
# Memory 14.1 MB Beats 79.61%
46+
class Custom:
47+
def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
48+
prefix_sums, n, m = sorted(nums), len(nums), len(queries)
49+
for i in range(1, n):
50+
prefix_sums[i] += prefix_sums[i - 1]
51+
res = [None] * m
52+
for i in range(m):
53+
l, r = 0, n
54+
while l < r:
55+
mid = (l + r) // 2
56+
if prefix_sums[mid] <= queries[i]:
57+
l = mid + 1
58+
else:
59+
r = mid
60+
res[i] = l
61+
return res
62+
63+
64+
def test():
65+
executors = [
66+
Pythonic,
67+
Custom,
68+
]
69+
tests = [
70+
[[2, 3, 4, 5], [1], [0]],
71+
[[4, 5, 2, 1], [3, 10, 21], [2, 3, 4]],
72+
]
73+
for executor in executors:
74+
start = timeit.default_timer()
75+
for _ in range(1):
76+
for col, t in enumerate(tests):
77+
sol = executor()
78+
result = sol.answerQueries(t[0], t[1])
79+
exp = t[2]
80+
assert result == exp, (
81+
f"\033[93m» {result} <> {exp}\033[91m for"
82+
+ f" test {col} using \033[1m{executor.__name__}"
83+
)
84+
stop = timeit.default_timer()
85+
used = str(round(stop - start, 5))
86+
cols = "{0:20}{1:10}{2:10}"
87+
res = cols.format(executor.__name__, used, "seconds")
88+
print(f"\033[92m» {res}\033[0m")
89+
90+
91+
test()

0 commit comments

Comments
 (0)