Skip to content

Commit b19f688

Browse files
committed
add python solutions for prefix sum pattern
1 parent cc52cb5 commit b19f688

File tree

3 files changed

+353
-1
lines changed

3 files changed

+353
-1
lines changed

python/contiguous-array.md

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,139 @@
1-
Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.
1+
2+
# Contiguous Array
3+
Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.
4+
5+
### Constraints:
6+
- 1 <= nums.length <= 10^5
7+
- nums[i] is either 0 or 1.
8+
9+
### Examples
10+
```javascript
11+
Input: nums = [0,1]
12+
Output: 2
13+
Explanation: [0, 1] is the longest contiguous subarray with an equal number of 0 and 1.
14+
15+
Input: nums = [0,1,0]
16+
Output: 2
17+
Explanation: [0, 1] (or [1, 0]) is a contiguous subarray with equal number of 0 and 1.
18+
```
19+
20+
## Approaches to Solve the Problem
21+
### Approach 1: Brute Force
22+
##### Intuition:
23+
In the brute-force solution, we try every possible subarray and check if it has an equal number of 0s and 1s. For each subarray, count the number of 0s and 1s, and if they are equal, update the maximum length.
24+
25+
- Loop over every subarray starting at each index.
26+
- For each subarray, count the number of 0s and 1s.
27+
- Update the maximum length if the number of 0s and 1s are equal.
28+
##### Time Complexity:
29+
O(n^2), where n is the length of the array. This is because we check every possible subarray.
30+
##### Space Complexity:
31+
O(1), no extra space is used.
32+
##### Python Code:
33+
```python
34+
def findMaxLength(nums: List[int]) -> int:
35+
max_len = 0
36+
n = len(nums)
37+
38+
for i in range(n):
39+
zeroes = ones = 0
40+
for j in range(i, n):
41+
if nums[j] == 0:
42+
zeroes += 1
43+
else:
44+
ones += 1
45+
if zeroes == ones:
46+
max_len = max(max_len, j - i + 1)
47+
48+
return max_len
49+
```
50+
### Approach 2: Prefix Sum with Hash Map
51+
##### Intuition:
52+
To optimize the solution, we can use the prefix sum technique. The key observation is that if we treat 0 as -1, then finding a subarray with an equal number of 0s and 1s is equivalent to finding a subarray whose sum is 0. Using this insight:
53+
54+
- Convert 0 to -1.
55+
- Use a hash map (sum_index) to store the first occurrence of each cumulative sum.
56+
- If the same sum appears again at a later index, it means the subarray between these two indices has a sum of 0.
57+
- Calculate the length of this subarray and update the maximum length.
58+
##### Visualization:
59+
For nums = [0, 1, 0]:
60+
61+
- Convert nums to [-1, 1, -1].
62+
- Maintain a prefix_sum and check:
63+
- At index 0: prefix_sum = -1, store it in the hash map.
64+
- At index 1: prefix_sum = 0, subarray from index 0 to 1 has sum 0.
65+
- At index 2: prefix_sum = -1, found the same sum at index 0, so the subarray from index 1 to 2 has sum 0.
66+
67+
Steps:
68+
1. Initialize prefix_sum as 0 and store prefix_sum = 0 at index -1 in the hash map.
69+
2. Traverse through the array, updating prefix_sum by adding -1 for 0 and 1 for 1.
70+
3. Check if the current prefix_sum has been seen before:
71+
- If yes, calculate the subarray length from the previous index to the current index and update the maximum length.
72+
- If no, store the current prefix_sum and index in the hash map.
73+
##### Time Complexity:
74+
O(n), where n is the length of the array. We traverse the array once.
75+
##### Space Complexity:
76+
O(n), because we use a hash map to store the prefix sums.
77+
##### Python Code:
78+
```python
79+
def findMaxLength(nums: List[int]) -> int:
80+
# Convert 0 to -1 and then find subarray with sum 0
81+
sum_index = {0: -1} # Store the first occurrence of each prefix_sum
82+
prefix_sum = 0
83+
max_len = 0
84+
85+
for i, num in enumerate(nums):
86+
# Convert 0 to -1
87+
if num == 0:
88+
prefix_sum += -1
89+
else:
90+
prefix_sum += 1
91+
92+
if prefix_sum in sum_index:
93+
# If the prefix_sum was seen before, calculate subarray length
94+
max_len = max(max_len, i - sum_index[prefix_sum])
95+
else:
96+
# Store the index of the first occurrence of this prefix_sum
97+
sum_index[prefix_sum] = i
98+
99+
return max_len
100+
```
101+
### Approach 3: Optimized Prefix Sum with Early Exit
102+
##### Intuition:
103+
This is an optimization over the previous approach, where we exit early if we find the maximum possible subarray (i.e., the array itself or the longest subarray so far). This approach doesn't improve time complexity in the worst case but can be useful in certain cases.
104+
105+
1. As we build the prefix_sum, we track the length of the longest valid subarray.
106+
2. If the longest subarray already equals half of the current length, we can exit early.
107+
##### Time Complexity:
108+
O(n), since we still traverse the array once.
109+
##### Space Complexity:
110+
O(n), due to the hash map.
111+
##### Python Code:
112+
```python
113+
def findMaxLength(nums: List[int]) -> int:
114+
sum_index = {0: -1}
115+
prefix_sum = 0
116+
max_len = 0
117+
118+
for i, num in enumerate(nums):
119+
prefix_sum += -1 if num == 0 else 1
120+
121+
if prefix_sum in sum_index:
122+
max_len = max(max_len, i - sum_index[prefix_sum])
123+
# Early exit if the maximum possible subarray is found
124+
if max_len == i + 1:
125+
break
126+
else:
127+
sum_index[prefix_sum] = i
128+
129+
return max_len
130+
```
131+
## Summary
132+
133+
| Approach | Time Complexity | Space Complexity |
134+
|-----------------------------------|-----------------|------------------|
135+
| Brute Force | O(n^2) | O(1) |
136+
| Prefix Sum with Hash Map | O(n) | O(n) |
137+
| Optimized Prefix Sum with Early Exit | O(n) | O(n) |
138+
139+
The Prefix Sum with Hash Map approach is the most efficient solution as it allows finding the maximum length subarray in linear time using constant updates to the prefix sum and hash map.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Range Sum Query - Immutable
2+
Given an integer array nums, handle multiple queries of the following type:
3+
4+
Calculate the sum of the elements of nums between indices left and right inclusive, where left <= right.
5+
Implement the NumArray class:
6+
7+
NumArray(int[] nums) Initializes the object with the integer array nums.
8+
int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e., nums[left] + nums[left + 1] + ... + nums[right]).
9+
10+
### Constraints:
11+
- 1 <= nums.length <= 10^4
12+
- -10^5 <= nums[i] <= 10^5
13+
- 0 <= left <= right < nums.length
14+
- At most 10^4 calls will be made to sumRange.
15+
16+
### Examples
17+
```javascript
18+
Input:
19+
["NumArray", "sumRange", "sumRange", "sumRange"]
20+
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
21+
22+
Output:
23+
[null, 1, -1, -3]
24+
25+
Explanation:
26+
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
27+
numArray.sumRange(0, 2); // return 1 (-2 + 0 + 3 = 1)
28+
numArray.sumRange(2, 5); // return -1 (3 + -5 + 2 + -1 = -1)
29+
numArray.sumRange(0, 5); // return -3 (-2 + 0 + 3 + -5 + 2 + -1 = -3)
30+
```
31+
32+
## Approaches to Solve the Problem
33+
### Approach 1: Brute Force (Iterate Over Range)
34+
##### Intuition:
35+
The most straightforward approach is to iterate over the elements between indices left and right every time sumRange is called. This solution will work, but it may not be efficient if sumRange is called multiple times.
36+
37+
Steps:
38+
1. Each time sumRange(left, right) is called, iterate over the elements from left to right and sum them.
39+
2. Return the sum after iterating over the range.
40+
##### Time Complexity:
41+
O(n) for each query, where n is the length of the range between left and right.
42+
##### Space Complexity:
43+
O(1), no extra space is used beyond a few variables for the sum.
44+
##### Python Code:
45+
```python
46+
class NumArray:
47+
48+
def __init__(self, nums: List[int]):
49+
self.nums = nums # Store the input array
50+
51+
def sumRange(self, left: int, right: int) -> int:
52+
# Calculate the sum by iterating over the range
53+
return sum(self.nums[left:right + 1])
54+
```
55+
### Approach 2: Prefix Sum (Preprocessing)
56+
##### Intuition:
57+
To optimize the sum range queries, we can use the concept of prefix sums. The idea is to preprocess the array and compute a cumulative sum array where each element at index i stores the sum of elements from the start of the array up to i. This allows us to compute any range sum in constant time.
58+
59+
- Define prefix_sum[i] as the sum of elements nums[0] + nums[1] + ... + nums[i-1].
60+
- To find the sum of the range [left, right], we can calculate:
61+
```python
62+
sumRange(left, right) = prefix_sum[right + 1] - prefix_sum[left]
63+
```
64+
65+
Steps:
66+
67+
1. Precompute the cumulative sum array prefix_sum where each entry stores the sum of all elements before index i.
68+
2. Use this precomputed array to quickly answer range sum queries by subtracting two prefix sums.
69+
70+
##### Why This Works:
71+
If a cycle exists, the fast pointer, moving twice as fast as the slow pointer, will "lap" the slow pointer and meet it again inside the cycle.
72+
##### Time Complexity:
73+
- O(n) for preprocessing the prefix sum array.
74+
- O(1) for each query, since we only perform subtraction of two values.
75+
##### Space Complexity:
76+
O(n), for storing the prefix sum array.
77+
##### Visualization:
78+
```rust
79+
For nums = [-2, 0, 3, -5, 2, -1], the prefix_sum array would be:
80+
prefix_sum = [0, -2, -2, 1, -4, -2, -3]
81+
82+
To compute sumRange(0, 2), we do:
83+
sumRange(0, 2) = prefix_sum[3] - prefix_sum[0] = 1 - 0 = 1
84+
```
85+
##### Python Code:
86+
```python
87+
class NumArray:
88+
89+
def __init__(self, nums: List[int]):
90+
# Create a prefix sum array
91+
self.prefix_sum = [0] * (len(nums) + 1)
92+
for i in range(1, len(self.prefix_sum)):
93+
self.prefix_sum[i] = self.prefix_sum[i - 1] + nums[i - 1]
94+
95+
def sumRange(self, left: int, right: int) -> int:
96+
# Calculate the sum for the range using the prefix sum
97+
return self.prefix_sum[right + 1] - self.prefix_sum[left]
98+
```
99+
## Summary
100+
101+
| Approach | Time Complexity | Space Complexity |
102+
|-----------------------------------|-----------------|------------------|
103+
| Brute Force (Iterate Over Range) | O(n) per query | O(1) |
104+
| Prefix Sum (Preprocessing) | O(n) to preprocess, O(1) per query | O(n) for storing prefix sum |
105+
106+
The Prefix Sum approach is the most optimal solution as it allows querying the sum of any range in constant time after an initial preprocessing step.

python/subarray-sum-equals-k.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
2+
# Subarray Sum Equals K
3+
Given an array of integers nums and an integer k, return the total number of continuous subarrays whose sum equals to k.
4+
5+
### Constraints:
6+
- 1 <= nums.length <= 2 * 10^4
7+
- -1000 <= nums[i] <= 1000
8+
- -10^7 <= k <= 10^7
9+
10+
### Examples
11+
```javascript
12+
Input: nums = [1,1,1], k = 2
13+
Output: 2
14+
Explanation: [1,1] and [1,1] are the two subarrays with a sum equal to 2.
15+
16+
Input: nums = [1,2,3], k = 3
17+
Output: 2
18+
Explanation: [1, 2] and [3] are the two subarrays with a sum equal to 3.
19+
```
20+
21+
## Approaches to Solve the Problem
22+
### Approach 1: Brute Force
23+
##### Intuition:
24+
A straightforward approach would be to iterate through every possible subarray in nums and check if the sum equals k. This solution works but is inefficient due to the repeated summation of subarray elements.
25+
26+
1. Use a nested loop to consider every possible subarray in the array.
27+
2. For each subarray, calculate the sum.
28+
3. If the sum equals k, increment the count of valid subarrays.
29+
##### Time Complexity:
30+
O(n²), where n is the length of the array because we consider all subarrays.
31+
##### Space Complexity:
32+
O(1), no extra space is used beyond the variables for sum and count.
33+
##### Python Code:
34+
```python
35+
def subarraySum(nums: List[int], k: int) -> int:
36+
count = 0
37+
n = len(nums)
38+
39+
for i in range(n):
40+
current_sum = 0
41+
for j in range(i, n):
42+
current_sum += nums[j]
43+
if current_sum == k:
44+
count += 1
45+
46+
return count
47+
```
48+
### Approach 2: Prefix Sum with Hash Map
49+
##### Intuition:
50+
The key idea in optimizing the brute force approach is to use prefix sums. A prefix sum is the cumulative sum of elements from the start of the array to any index i. If the difference between two prefix sums equals k, then the subarray between these two indices has a sum of k.
51+
52+
1. Define prefix_sum[i] as the cumulative sum of elements from the start of the array to index i.
53+
2. To find the number of subarrays with sum k, check if there is a previous prefix sum such that: ```current_prefix_sum - previous_prefix_sum = k```
54+
3. Rearranging this gives: ```previous_prefix_sum = current_prefix_sum - k```
55+
4. Use a hash map to store the frequency of each prefix sum as we iterate through the array.
56+
57+
Steps:
58+
1. Initialize a prefix_sum as 0 and a hash map prefix_sum_count to store how often each prefix sum occurs.
59+
2. For each element in the array, update the prefix_sum.
60+
3. Check if prefix_sum - k exists in the hash map. If it does, the count of valid subarrays is incremented by the frequency of this sum.
61+
4. Add the current prefix_sum to the hash map.
62+
63+
##### Visualization:
64+
For nums = [1, 1, 1], k = 2:
65+
66+
```perl
67+
prefix_sum = 0 initially
68+
Iterating through the array:
69+
At index 0, prefix_sum = 1, prefix_sum - k = 1 - 2 = -1 (not found in hash map)
70+
At index 1, prefix_sum = 2, prefix_sum - k = 2 - 2 = 0 (found in hash map, count += 1)
71+
At index 2, prefix_sum = 3, prefix_sum - k = 3 - 2 = 1 (found in hash map, count += 1)
72+
73+
Total count = 2
74+
```
75+
##### Time Complexity:
76+
O(n), where n is the length of the array, since we traverse the array once and use hash map operations that are O(1) on average.
77+
##### Space Complexity:
78+
O(n), because we use a hash map to store prefix sums.
79+
##### Python Code:
80+
```python
81+
def subarraySum(nums: List[int], k: int) -> int:
82+
prefix_sum_count = {0: 1} # To handle the case when prefix_sum equals k
83+
prefix_sum = 0
84+
count = 0
85+
86+
for num in nums:
87+
prefix_sum += num
88+
89+
# Check if there is a previous prefix_sum such that prefix_sum - k exists
90+
if prefix_sum - k in prefix_sum_count:
91+
count += prefix_sum_count[prefix_sum - k]
92+
93+
# Add the current prefix_sum to the map
94+
if prefix_sum in prefix_sum_count:
95+
prefix_sum_count[prefix_sum] += 1
96+
else:
97+
prefix_sum_count[prefix_sum] = 1
98+
99+
return count
100+
```
101+
## Summary
102+
103+
| Approach | Time Complexity | Space Complexity |
104+
|-----------------------------------|-----------------|------------------|
105+
| Brute Force | O(n^2) | O(1) |
106+
| Prefix Sum with Hash Map | O(n) | O(n) |
107+
108+
The Prefix Sum with Hash Map approach is the most efficient solution as it computes the number of valid subarrays in linear time, making use of cumulative sums and hash maps to store and check the required prefix sums.

0 commit comments

Comments
 (0)