Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@
* [Test Longest Substring K Repeating Chars](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/longest_substring_with_k_repeating_chars/test_longest_substring_k_repeating_chars.py)
* Longest Substring Without Repeating Characters
* [Test Longest Substring Without Repeating Characters](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/longest_substring_without_repeating_characters/test_longest_substring_without_repeating_characters.py)
* Max Points From Cards
* [Test Max Points From Cards](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/max_points_from_cards/test_max_points_from_cards.py)
* Max Sum Of Subarray
* [Test Max Sum Sub Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/max_sum_of_subarray/test_max_sum_sub_array.py)
* Repeated Dna Sequences
* [Test Repeated Dna Sequences](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/repeated_dna_sequences/test_repeated_dna_sequences.py)
* Sorting
Expand Down
101 changes: 101 additions & 0 deletions algorithms/sliding_window/max_points_from_cards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Max Points You Can Obtain From Cards

Given an array of integers representing card values, write a function to calculate the maximum score you can achieve by
picking exactly k cards.

You must pick cards in order from either end. You can take some cards from the beginning, then switch to taking cards
from the end, but you cannot skip cards or pick from the middle.

For example, with k = 3:

- Take the first 3 cards: valid
- Take the last 3 cards: valid
- Take the first card, then the last 2 cards: valid
- Take the first 2 cards, then the last card: valid
- Take card at index 0, skip some, then take card at index 5: not valid (skipping cards)

## Constraints

- 1 <= k <= cards.length
- 1 <= `cards.length` <= 10^5
- 1 <= `cards[i]` <= 10^4

## Examples

Example 1:
```text
Input: cardPoints = [1,2,3,4,5,6,1], k = 3
Output: 12
Explanation: After the first step, your score will always be 1. However, choosing the rightmost card first will maximize
your total score. The optimal strategy is to take the three cards on the right, giving a final score of 1 + 6 + 5 = 12.
```

Example 2:
```text
Input: cardPoints = [2,2,2], k = 2
Output: 4
Explanation: Regardless of which two cards you take, your score will always be 4.
```

Example 3:
```text
Input: cardPoints = [9,7,7,9,7,7,9], k = 7
Output: 55
Explanation: You have to take all the cards. Your score is the sum of points of all cards.
```

## Topics

- Array
- Sliding Window
- Prefix Sum

## Solutions

When you pick k cards from either end of the array, you're actually leaving behind n - k consecutive cards in the middle
that you didn't pick.

For example, with cards = [2,11,4,5,3,9,2] and k = 3:

![Solution 1](./images/solutions/max_points_from_cards_solution_1.png)

Every possible way to pick 3 cards from the ends corresponds to a different window of 4 cards (n - k = 7 - 3 = 4) in the
middle that we're NOT picking.

### Why This Matters

Since we know the total sum of all cards, we can calculate:

Sum of picked cards = Total sum - Sum of unpicked cards

So to maximize the sum of picked cards, we need to minimize the sum of unpicked cards.

This transforms the problem: instead of trying all combinations of picking from ends, we find the minimum sum of any
window of size n - k.

![Solution 2](./images/solutions/max_points_from_cards_solution_2.png)

### Algorithm

Use a fixed-length sliding window of size n - k to find the minimum sum of any consecutive n - k cards. For each window
position, calculate total - window_sum to get the corresponding score, and track the maximum.

![Solution 3](./images/solutions/max_points_from_cards_solution_3.png)
![Solution 4](./images/solutions/max_points_from_cards_solution_4.png)
![Solution 5](./images/solutions/max_points_from_cards_solution_5.png)
![Solution 6](./images/solutions/max_points_from_cards_solution_6.png)
![Solution 7](./images/solutions/max_points_from_cards_solution_7.png)
![Solution 8](./images/solutions/max_points_from_cards_solution_8.png)
![Solution 9](./images/solutions/max_points_from_cards_solution_9.png)
![Solution 10](./images/solutions/max_points_from_cards_solution_10.png)
![Solution 11](./images/solutions/max_points_from_cards_solution_11.png)
![Solution 12](./images/solutions/max_points_from_cards_solution_12.png)
![Solution 13](./images/solutions/max_points_from_cards_solution_13.png)
![Solution 14](./images/solutions/max_points_from_cards_solution_14.png)
![Solution 15](./images/solutions/max_points_from_cards_solution_15.png)
![Solution 16](./images/solutions/max_points_from_cards_solution_16.png)
![Solution 17](./images/solutions/max_points_from_cards_solution_17.png)
![Solution 18](./images/solutions/max_points_from_cards_solution_18.png)
![Solution 19](./images/solutions/max_points_from_cards_solution_19.png)
![Solution 20](./images/solutions/max_points_from_cards_solution_20.png)
![Solution 21](./images/solutions/max_points_from_cards_solution_21.png)
32 changes: 32 additions & 0 deletions algorithms/sliding_window/max_points_from_cards/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import List


def max_score(card_points: List[int], k: int) -> int:
number_of_cards = len(card_points)
total_points = sum(card_points)

# If we have to pick all the cards or more cards that we have been provided with, then the max score is the total
# of all the cards
if k >= number_of_cards:
return total_points

# Maintain a state or the window sum to calculate the sum of the cards in the window
window_sum = 0
# Max points is the score we get from the cards we pick, this is what we eventually return
max_points = 0
# This will keep track of the index of the card to remove from the current window as we traverse the cards
start = 0

for end in range(number_of_cards):
# Add the card at the end of the window to the window sum
window_sum += card_points[end]

# Calculates if we have a valid window to calculate the max points
if end - start + 1 == number_of_cards - k:
max_points = max(max_points, total_points - window_sum)
# Contract the window, by removing the card at the start of the window
window_sum -= card_points[start]
# Move to the next index
start += 1

return max_points
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.sliding_window.max_points_from_cards import max_score


MAX_POINTS_FROM_PICKING_K_CARDS = [
([1, 2, 3, 4, 5, 6, 1], 3, 12),
([2, 2, 2], 2, 4),
([9, 7, 7, 9, 7, 7, 9], 7, 55),
([2, 11, 4, 5, 3, 9, 2], 3, 17),
([1, 100, 10, 0, 4, 5, 6], 3, 111),
([1, 1000, 1], 1, 1),
([1, 79, 80, 1, 1, 1, 200, 1], 3, 202),
([100, 40, 17, 9, 73, 75], 3, 248),
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5, 40),
]


class MaxPointsFromPickingKCardsTestCase(unittest.TestCase):
@parameterized.expand(MAX_POINTS_FROM_PICKING_K_CARDS)
def test_max_points_from_k_cards(self, cards: List[int], k: int, expected: int):
actual = max_score(cards, k)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
38 changes: 38 additions & 0 deletions algorithms/sliding_window/max_sum_of_subarray/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Maximum Sum of Subarray with Size K

Given an array of integers nums and an integer k, find the maximum sum of any contiguous subarray of size k.

## Examples

Example 1

```text
Input: nums = [2, 1, 5, 1, 3, 2], k = 3
Output: 9
Explanation: The subarray with the maximum sum is [5, 1, 3] with a sum of 9.
```

## Solution

We start by extending the window to size k. Whenever our window is of size k, we first compute the sum of the window and
update max_sum if it is larger than max_sum. Then, we contract the window by removing the leftmost element to prepare for
the next iteration. Note how we calculate the sum of the window incrementally by adding the new element and removing from
the previous sum.

![Solution 1](./images/solutions/max_sum_of_subarray_size_k_solution_1.png)
![Solution 2](./images/solutions/max_sum_of_subarray_size_k_solution_2.png)
![Solution 3](./images/solutions/max_sum_of_subarray_size_k_solution_3.png)
![Solution 4](./images/solutions/max_sum_of_subarray_size_k_solution_4.png)
![Solution 5](./images/solutions/max_sum_of_subarray_size_k_solution_5.png)
![Solution 6](./images/solutions/max_sum_of_subarray_size_k_solution_6.png)
![Solution 7](./images/solutions/max_sum_of_subarray_size_k_solution_7.png)
![Solution 8](./images/solutions/max_sum_of_subarray_size_k_solution_8.png)
![Solution 9](./images/solutions/max_sum_of_subarray_size_k_solution_9.png)
![Solution 10](./images/solutions/max_sum_of_subarray_size_k_solution_10.png)
![Solution 11](./images/solutions/max_sum_of_subarray_size_k_solution_11.png)
![Solution 12](./images/solutions/max_sum_of_subarray_size_k_solution_12.png)
![Solution 13](./images/solutions/max_sum_of_subarray_size_k_solution_13.png)
![Solution 14](./images/solutions/max_sum_of_subarray_size_k_solution_14.png)
![Solution 15](./images/solutions/max_sum_of_subarray_size_k_solution_15.png)
![Solution 16](./images/solutions/max_sum_of_subarray_size_k_solution_16.png)
![Solution 17](./images/solutions/max_sum_of_subarray_size_k_solution_17.png)
64 changes: 64 additions & 0 deletions algorithms/sliding_window/max_sum_of_subarray/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from typing import List


def max_sum_subarray(nums: List[int], k: int) -> int:
n = len(nums)

# If the length of the numbers is less than k, we return 0, as we can't get a contiguous subarray of size k from nums
if n < k:
return 0

start = 0
state = 0
max_sum = 0

for end in range(n):
state += nums[end]

if end - start + 1 == k:
max_sum = max(max_sum, state)
state -= nums[start]
start += 1

return max_sum


def max_sum_subarray_2(nums: List[int], k: int) -> int:
n = len(nums)

# If the length of the numbers is less than k, we return 0, as we can't get a contiguous subarray of size k from nums
if n < k:
return 0

window_sum = sum(nums[:k])
max_sum = window_sum
start = 0

for end in range(k, n):
window_sum += nums[end] - nums[start]
start += 1
max_sum = max(max_sum, window_sum)

return max_sum


def max_sum_subarray_3(nums: List[int], k: int) -> int:
n = len(nums)

# If the length of the numbers is less than k, we return 0, as we can't get a contiguous subarray of size k from nums
if n < k:
return 0

# Use two points which will act as the boundary of the window. In this window, the sum of the elements will be
# checked to get the max sum of the sub array
lower_bound = 0
upper_bound = k
max_sum = 0

while upper_bound <= n:
current_sum = sum(nums[lower_bound:upper_bound])
max_sum = max(current_sum, max_sum)
lower_bound += 1
upper_bound += 1

return max_sum
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.sliding_window.max_sum_of_subarray import (
max_sum_subarray,
max_sum_subarray_2,
max_sum_subarray_3,
)

MAX_SUM_SUBARRAY_OF_SIZE_K = [
([2, 1, 5, 1, 3, 2], 3, 9),
([4, 2, -1, 9, 7, -3, 5], 4, 18),
([4, 2, 4, 5, 6], 4, 17),
([1, 2, 3, 4, 5], 2, 9),
([1, 1, 1, 1, 1], 1, 1),
([5, 5, 5, 5, 5], 3, 15),
([1, 2, 3, 1, 2, 3], 3, 6),
([1, 2, 1, 3, 1, 1, 1], 3, 6),
([1, 2, 3, 4, 5, 6], 5, 20),
]


class MaxSumSubArrayOfSizeKTestCase(unittest.TestCase):
@parameterized.expand(MAX_SUM_SUBARRAY_OF_SIZE_K)
def test_max_sum_subarray_size_k(self, nums: List[int], k: int, expected: int):
actual = max_sum_subarray(nums, k)
self.assertEqual(expected, actual)

@parameterized.expand(MAX_SUM_SUBARRAY_OF_SIZE_K)
def test_max_sum_subarray_size_k_2(self, nums: List[int], k: int, expected: int):
actual = max_sum_subarray_2(nums, k)
self.assertEqual(expected, actual)

@parameterized.expand(MAX_SUM_SUBARRAY_OF_SIZE_K)
def test_max_sum_subarray_size_k_3(self, nums: List[int], k: int, expected: int):
actual = max_sum_subarray_3(nums, k)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()