From f4fe48bf621c9eeb3c69d0d69e84bdb291503dd8 Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Mon, 24 Feb 2025 20:00:08 +0530 Subject: [PATCH 01/10] Create prefix_sum.py --- dynamic_programming/prefix_sum.py | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 dynamic_programming/prefix_sum.py diff --git a/dynamic_programming/prefix_sum.py b/dynamic_programming/prefix_sum.py new file mode 100644 index 000000000000..ccf7ec6987d2 --- /dev/null +++ b/dynamic_programming/prefix_sum.py @@ -0,0 +1,90 @@ +""" +Author : Sanjay Muthu + +This is an implementation of the Dynamic Programming solution to the Range Sum Query problem. + +The problem statement is: + Given an array and q queries, + each query stating you to find the sum of elements l to r (inclusive) (l and r are given in the query) + +Example: + arr = [1, 4, 6, 2, 61, 12] + queries = 3 + l_1 = 2, r_1 = 5 + l_2 = 1, r_2 = 5 + l_3 = 3, r_3 = 4 + + as input will return + + [81, 85, 63] + + as output + + [0-indexing] + +NOTE:- 0-indexing means the indexing of the array starts from 0 +Example:- a = [1, 2, 3, 4, 5, 6] + Here, the 0th index of a is 1, + the 1st index of a is 2, + and so forth + +Time Complexity:- O(N + Q) +-- O(N) pre-calculation time to calculate the prefix sum array +-- and O(1) time per each query = O(1 * Q) = O(Q) time + +Space Complexity:- O(N) +-- O(N) to store the prefix sum + +Algorithm:- +So first we calculate the prefix sum (dp) of the array. +The prefix sum of the index i is the sum of all elements indexed from 0 to i (inclusive) +The prefix sum of the index i is the prefix sum of index i-1 + the current element. +So, The state of the dp is dp[i] = dp[i-1] + a[i]; + +After we calculate the prefix sum, +For each query [l, r] +The answer is dp[r]-dp[l-1] ( we need to be careful because l might be 0 ) +For example take this array:- + [4, 2, 1, 6, 3] +The prefix sum calculated for this array would be:- + [4, 4+2, 4+2+1, 4+2+1+6, 4+2+1+6+3] +==> [4, 6, 7, 13, 16] +If the query was l=3, r=4, +The answer would be 6+3 = 9 but this would require O(r-l) time ≈ O(N) time +If we use prefix sums we can find it in O(1) time by using the formula prefix[r]-prefix[l-1] +This formula works because prefix[r] is the sum of elements from [0, r] and prefix[l-1] is the sum of elements from [0, l-1], +so if we do prefix[r]-prefix[l-1] it will be [0, r] - [0, l-1] = [0, l-1] + [l, r] - [0, l-1] = [l, r] +""" + +from __future__ import annotations + + +def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: + """ + Some examples + >>> prefix_sum([1, 4, 6, 2, 61, 12], [(2, 5), (1, 5), (3, 4)]) + [81, 85, 63] + >>> prefix_sum([4, 2, 1, 6, 3], [(3, 4), (1, 3), (0, 2)]) + [9, 9, 7] + """ + # The prefix sum array + dp = [0] * len(array) + dp[0] = array[0] + for i in range(1, len(array)): + dp[i] = dp[i-1] + array[i] + + # Read Algorithm section (Line 38) + result = [] + for query in queries: + res = dp[query[1]] + if query[0] != 0: + res -= dp[query[0]-1] + result.append(res) + + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 463e6aaece9962d7b9f934082e03776d2c3c75fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:31:08 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dynamic_programming/prefix_sum.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/prefix_sum.py b/dynamic_programming/prefix_sum.py index ccf7ec6987d2..d2dc0f0394b4 100644 --- a/dynamic_programming/prefix_sum.py +++ b/dynamic_programming/prefix_sum.py @@ -71,14 +71,14 @@ def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: dp = [0] * len(array) dp[0] = array[0] for i in range(1, len(array)): - dp[i] = dp[i-1] + array[i] + dp[i] = dp[i - 1] + array[i] # Read Algorithm section (Line 38) result = [] for query in queries: res = dp[query[1]] if query[0] != 0: - res -= dp[query[0]-1] + res -= dp[query[0] - 1] result.append(res) return result From 180b28f7687db630a240798c94f18bce1756cb8c Mon Sep 17 00:00:00 2001 From: Sanjay Muthu Date: Mon, 24 Feb 2025 20:11:38 +0530 Subject: [PATCH 03/10] Fix pre-commit and ruff errors --- dynamic_programming/prefix_sum.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dynamic_programming/prefix_sum.py b/dynamic_programming/prefix_sum.py index d2dc0f0394b4..8f01fc8f671e 100644 --- a/dynamic_programming/prefix_sum.py +++ b/dynamic_programming/prefix_sum.py @@ -1,11 +1,11 @@ """ Author : Sanjay Muthu -This is an implementation of the Dynamic Programming solution to the Range Sum Query problem. +This is an implementation of the Dynamic Programming solution to the Range Sum Query. The problem statement is: Given an array and q queries, - each query stating you to find the sum of elements l to r (inclusive) (l and r are given in the query) + each query stating you to find the sum of elements l to r (inclusive) Example: arr = [1, 4, 6, 2, 61, 12] @@ -51,9 +51,14 @@ ==> [4, 6, 7, 13, 16] If the query was l=3, r=4, The answer would be 6+3 = 9 but this would require O(r-l) time ≈ O(N) time -If we use prefix sums we can find it in O(1) time by using the formula prefix[r]-prefix[l-1] -This formula works because prefix[r] is the sum of elements from [0, r] and prefix[l-1] is the sum of elements from [0, l-1], -so if we do prefix[r]-prefix[l-1] it will be [0, r] - [0, l-1] = [0, l-1] + [l, r] - [0, l-1] = [l, r] + + +If we use prefix sums we can find it in O(1) by using the formula prefix[r]-prefix[l-1] +This formula works because prefix[r] is the sum of elements from [0, r] + and prefix[l-1] is the sum of elements from [0, l-1], +so if we do prefix[r]-prefix[l-1] it will be [0, r] - [0, l-1] + = [0, l-1] + [l, r] - [0, l-1] + = [l, r] """ from __future__ import annotations @@ -71,14 +76,14 @@ def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: dp = [0] * len(array) dp[0] = array[0] for i in range(1, len(array)): - dp[i] = dp[i - 1] + array[i] + dp[i] = dp[i-1] + array[i] # Read Algorithm section (Line 38) result = [] for query in queries: res = dp[query[1]] if query[0] != 0: - res -= dp[query[0] - 1] + res -= dp[query[0]-1] result.append(res) return result From 175dc9c89d4e61d9e1759f02825af0b5ed9ca6d7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:49:44 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dynamic_programming/prefix_sum.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/prefix_sum.py b/dynamic_programming/prefix_sum.py index 8f01fc8f671e..b04f84cd36a4 100644 --- a/dynamic_programming/prefix_sum.py +++ b/dynamic_programming/prefix_sum.py @@ -76,14 +76,14 @@ def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: dp = [0] * len(array) dp[0] = array[0] for i in range(1, len(array)): - dp[i] = dp[i-1] + array[i] + dp[i] = dp[i - 1] + array[i] # Read Algorithm section (Line 38) result = [] for query in queries: res = dp[query[1]] if query[0] != 0: - res -= dp[query[0]-1] + res -= dp[query[0] - 1] result.append(res) return result From 526a4509eed500f8207ad7dccc503079a53caffa Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 27 Feb 2025 14:02:49 +0300 Subject: [PATCH 05/10] Rename prefix_sum.py to range_sum_query.py --- dynamic_programming/{prefix_sum.py => range_sum_query.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dynamic_programming/{prefix_sum.py => range_sum_query.py} (100%) diff --git a/dynamic_programming/prefix_sum.py b/dynamic_programming/range_sum_query.py similarity index 100% rename from dynamic_programming/prefix_sum.py rename to dynamic_programming/range_sum_query.py From 619c9055673b1b68d96887476e4332c72c6c860e Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 27 Feb 2025 14:17:09 +0300 Subject: [PATCH 06/10] Refactor description --- dynamic_programming/range_sum_query.py | 63 ++++++++++++-------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py index b04f84cd36a4..21af96dc929e 100644 --- a/dynamic_programming/range_sum_query.py +++ b/dynamic_programming/range_sum_query.py @@ -1,11 +1,11 @@ """ -Author : Sanjay Muthu +Author: Sanjay Muthu This is an implementation of the Dynamic Programming solution to the Range Sum Query. The problem statement is: Given an array and q queries, - each query stating you to find the sum of elements l to r (inclusive) + each query stating you to find the sum of elements from l to r (inclusive) Example: arr = [1, 4, 6, 2, 61, 12] @@ -20,45 +20,42 @@ as output - [0-indexing] +0-indexing: +NOTE: 0-indexing means the indexing of the array starts from 0 +Example: a = [1, 2, 3, 4, 5, 6] + Here, the 0th index of a is 1, + the 1st index of a is 2, + and so forth -NOTE:- 0-indexing means the indexing of the array starts from 0 -Example:- a = [1, 2, 3, 4, 5, 6] - Here, the 0th index of a is 1, - the 1st index of a is 2, - and so forth +Time Complexity: O(N + Q) +* O(N) pre-calculation time to calculate the prefix sum array +* and O(1) time per each query = O(1 * Q) = O(Q) time -Time Complexity:- O(N + Q) --- O(N) pre-calculation time to calculate the prefix sum array --- and O(1) time per each query = O(1 * Q) = O(Q) time +Space Complexity: O(N) +* O(N) to store the prefix sum -Space Complexity:- O(N) --- O(N) to store the prefix sum - -Algorithm:- -So first we calculate the prefix sum (dp) of the array. -The prefix sum of the index i is the sum of all elements indexed from 0 to i (inclusive) -The prefix sum of the index i is the prefix sum of index i-1 + the current element. -So, The state of the dp is dp[i] = dp[i-1] + a[i]; +Algorithm: +So, first we calculate the prefix sum (dp) of the array. +The prefix sum of the index i is the sum of all elements indexed from 0 to i (inclusive). +The prefix sum of the index i is the prefix sum of index (i - 1) + the current element. +So, the state of the dp is dp[i] = dp[i - 1] + a[i]. After we calculate the prefix sum, -For each query [l, r] -The answer is dp[r]-dp[l-1] ( we need to be careful because l might be 0 ) -For example take this array:- +for each query [l, r] +the answer is dp[r] - dp[l - 1] (we need to be careful because l might be 0). +For example take this array: [4, 2, 1, 6, 3] -The prefix sum calculated for this array would be:- - [4, 4+2, 4+2+1, 4+2+1+6, 4+2+1+6+3] -==> [4, 6, 7, 13, 16] -If the query was l=3, r=4, -The answer would be 6+3 = 9 but this would require O(r-l) time ≈ O(N) time - +The prefix sum calculated for this array would be: + [4, 4 + 2, 4 + 2 + 1, 4 + 2 + 1 + 6, 4 + 2 + 1 + 6 + 3] + ==> [4, 6, 7, 13, 16] +If the query was l = 3, r = 4, +the answer would be 6 + 3 = 9 but this would require O(r - l + 1) time ≈ O(N) time -If we use prefix sums we can find it in O(1) by using the formula prefix[r]-prefix[l-1] +If we use prefix sums we can find it in O(1) by using the formula prefix[r] - prefix[l - 1]. This formula works because prefix[r] is the sum of elements from [0, r] - and prefix[l-1] is the sum of elements from [0, l-1], -so if we do prefix[r]-prefix[l-1] it will be [0, r] - [0, l-1] - = [0, l-1] + [l, r] - [0, l-1] - = [l, r] +and prefix[l - 1] is the sum of elements from [0, l - 1], +so if we do prefix[r] - prefix[l - 1] it will be +[0, r] - [0, l - 1] = [0, l - 1] + [l, r] - [0, l - 1] = [l, r] """ from __future__ import annotations From 60651f251cf2829e4e7d89f8f4ff4dabf3b275d0 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 27 Feb 2025 14:19:35 +0300 Subject: [PATCH 07/10] Fix --- dynamic_programming/range_sum_query.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py index 21af96dc929e..19b88ebaa0d8 100644 --- a/dynamic_programming/range_sum_query.py +++ b/dynamic_programming/range_sum_query.py @@ -36,7 +36,8 @@ Algorithm: So, first we calculate the prefix sum (dp) of the array. -The prefix sum of the index i is the sum of all elements indexed from 0 to i (inclusive). +The prefix sum of the index i is the sum of all elements indexed +from 0 to i (inclusive). The prefix sum of the index i is the prefix sum of index (i - 1) + the current element. So, the state of the dp is dp[i] = dp[i - 1] + a[i]. @@ -51,7 +52,8 @@ If the query was l = 3, r = 4, the answer would be 6 + 3 = 9 but this would require O(r - l + 1) time ≈ O(N) time -If we use prefix sums we can find it in O(1) by using the formula prefix[r] - prefix[l - 1]. +If we use prefix sums we can find it in O(1) by using the formula +prefix[r] - prefix[l - 1]. This formula works because prefix[r] is the sum of elements from [0, r] and prefix[l - 1] is the sum of elements from [0, l - 1], so if we do prefix[r] - prefix[l - 1] it will be From 49f366e6ebff7f83ad1b563f4c3ccf6e00b4b770 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 27 Feb 2025 14:24:42 +0300 Subject: [PATCH 08/10] Refactor code --- dynamic_programming/range_sum_query.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py index 19b88ebaa0d8..51b5e8dd9d43 100644 --- a/dynamic_programming/range_sum_query.py +++ b/dynamic_programming/range_sum_query.py @@ -59,13 +59,8 @@ so if we do prefix[r] - prefix[l - 1] it will be [0, r] - [0, l - 1] = [0, l - 1] + [l, r] - [0, l - 1] = [l, r] """ - -from __future__ import annotations - - def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: """ - Some examples >>> prefix_sum([1, 4, 6, 2, 61, 12], [(2, 5), (1, 5), (3, 4)]) [81, 85, 63] >>> prefix_sum([4, 2, 1, 6, 3], [(3, 4), (1, 3), (0, 2)]) @@ -77,12 +72,13 @@ def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: for i in range(1, len(array)): dp[i] = dp[i - 1] + array[i] - # Read Algorithm section (Line 38) + # See Algorithm section (Line 44) result = [] for query in queries: - res = dp[query[1]] - if query[0] != 0: - res -= dp[query[0] - 1] + l, r = query + res = dp[r] + if l > 0: + res -= dp[l - 1] result.append(res) return result From 49ea535a06372f031b836c2ed471d2c1b578fa69 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:25:04 +0000 Subject: [PATCH 09/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dynamic_programming/range_sum_query.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py index 51b5e8dd9d43..4b7416c9e9cd 100644 --- a/dynamic_programming/range_sum_query.py +++ b/dynamic_programming/range_sum_query.py @@ -59,6 +59,8 @@ so if we do prefix[r] - prefix[l - 1] it will be [0, r] - [0, l - 1] = [0, l - 1] + [l, r] - [0, l - 1] = [l, r] """ + + def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: """ >>> prefix_sum([1, 4, 6, 2, 61, 12], [(2, 5), (1, 5), (3, 4)]) From d11e0bf55b6d235b5c2e0f839ff8d0a638d32590 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Thu, 27 Feb 2025 14:26:07 +0300 Subject: [PATCH 10/10] Fix --- dynamic_programming/range_sum_query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dynamic_programming/range_sum_query.py b/dynamic_programming/range_sum_query.py index 4b7416c9e9cd..484fcf785fda 100644 --- a/dynamic_programming/range_sum_query.py +++ b/dynamic_programming/range_sum_query.py @@ -77,10 +77,10 @@ def prefix_sum(array: list[int], queries: list[tuple[int, int]]) -> list[int]: # See Algorithm section (Line 44) result = [] for query in queries: - l, r = query - res = dp[r] - if l > 0: - res -= dp[l - 1] + left, right = query + res = dp[right] + if left > 0: + res -= dp[left - 1] result.append(res) return result