diff --git a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java
index c05f1af4e327..dec813dd3213 100644
--- a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java
+++ b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java
@@ -1,14 +1,26 @@
package com.thealgorithms.others;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
/**
- * References: https://en.wikipedia.org/wiki/Streaming_algorithm
+ * Algorithm to find the maximum sum of a subarray of size K with all distinct
+ * elements.
*
- * This model involves computing the maximum sum of subarrays of a fixed size \( K \) from a stream of integers.
- * As the stream progresses, elements from the end of the window are removed, and new elements from the stream are added.
+ * This implementation uses a sliding window approach with a hash map to
+ * efficiently
+ * track element frequencies within the current window. The algorithm maintains
+ * a window
+ * of size K and slides it across the array, ensuring all elements in the window
+ * are distinct.
*
+ * Time Complexity: O(n) where n is the length of the input array
+ * Space Complexity: O(k) for storing elements in the hash map
+ *
+ * @see Streaming
+ * Algorithm
+ * @see Sliding
+ * Window
* @author Swarga-codes (https://github.com/Swarga-codes)
*/
public final class MaximumSumOfDistinctSubarraysWithLengthK {
@@ -16,54 +28,62 @@ private MaximumSumOfDistinctSubarraysWithLengthK() {
}
/**
- * Finds the maximum sum of a subarray of size K consisting of distinct elements.
+ * Finds the maximum sum of a subarray of size K consisting of distinct
+ * elements.
*
- * @param k The size of the subarray.
- * @param nums The array from which subarrays will be considered.
+ * The algorithm uses a sliding window technique with a frequency map to track
+ * the count of each element in the current window. A window is valid only if
+ * all K elements are distinct (frequency map size equals K).
*
- * @return The maximum sum of any distinct-element subarray of size K. If no such subarray exists, returns 0.
+ * @param k The size of the subarray. Must be non-negative.
+ * @param nums The array from which subarrays will be considered.
+ * @return The maximum sum of any distinct-element subarray of size K.
+ * Returns 0 if no such subarray exists or if k is 0 or negative.
+ * @throws IllegalArgumentException if k is negative
*/
public static long maximumSubarraySum(int k, int... nums) {
- if (nums.length < k) {
+ if (k <= 0 || nums == null || nums.length < k) {
return 0;
}
- long masSum = 0; // Variable to store the maximum sum of distinct subarrays
- long currentSum = 0; // Variable to store the sum of the current subarray
- Set currentSet = new HashSet<>(); // Set to track distinct elements in the current subarray
- // Initialize the first window
+ long maxSum = 0;
+ long currentSum = 0;
+ Map frequencyMap = new HashMap<>();
+
+ // Initialize the first window of size k
for (int i = 0; i < k; i++) {
currentSum += nums[i];
- currentSet.add(nums[i]);
+ frequencyMap.put(nums[i], frequencyMap.getOrDefault(nums[i], 0) + 1);
}
- // If the first window contains distinct elements, update maxSum
- if (currentSet.size() == k) {
- masSum = currentSum;
+
+ // Check if the first window has all distinct elements
+ if (frequencyMap.size() == k) {
+ maxSum = currentSum;
}
+
// Slide the window across the array
- for (int i = 1; i < nums.length - k + 1; i++) {
- // Update the sum by removing the element that is sliding out and adding the new element
- currentSum = currentSum - nums[i - 1];
- currentSum = currentSum + nums[i + k - 1];
- int j = i;
- boolean flag = false; // flag value which says that the subarray contains distinct elements
- while (j < i + k && currentSet.size() < k) {
- if (nums[i - 1] == nums[j]) {
- flag = true;
- break;
- } else {
- j++;
- }
- }
- if (!flag) {
- currentSet.remove(nums[i - 1]);
+ for (int i = k; i < nums.length; i++) {
+ // Remove the leftmost element from the window
+ int leftElement = nums[i - k];
+ currentSum -= leftElement;
+ int leftFrequency = frequencyMap.get(leftElement);
+ if (leftFrequency == 1) {
+ frequencyMap.remove(leftElement);
+ } else {
+ frequencyMap.put(leftElement, leftFrequency - 1);
}
- currentSet.add(nums[i + k - 1]);
- // If the current window has distinct elements, compare and possibly update maxSum
- if (currentSet.size() == k && masSum < currentSum) {
- masSum = currentSum;
+
+ // Add the new rightmost element to the window
+ int rightElement = nums[i];
+ currentSum += rightElement;
+ frequencyMap.put(rightElement, frequencyMap.getOrDefault(rightElement, 0) + 1);
+
+ // If all elements in the window are distinct, update maxSum if needed
+ if (frequencyMap.size() == k && currentSum > maxSum) {
+ maxSum = currentSum;
}
}
- return masSum; // the final maximum sum
+
+ return maxSum;
}
}
diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java
index f360e3f53546..1a42f1815a96 100644
--- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java
+++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java
@@ -3,20 +3,157 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-public class MaximumSumOfDistinctSubarraysWithLengthKTest {
+/**
+ * Test class for {@link MaximumSumOfDistinctSubarraysWithLengthK}.
+ *
+ * This class contains comprehensive test cases to verify the correctness of the
+ * maximum subarray sum algorithm with distinct elements constraint.
+ */
+class MaximumSumOfDistinctSubarraysWithLengthKTest {
+ /**
+ * Parameterized test for various input scenarios.
+ *
+ * @param expected the expected maximum sum
+ * @param k the subarray size
+ * @param arr the input array
+ */
@ParameterizedTest
@MethodSource("inputStream")
- void testMaximumSubarraySum(int expected, int k, int[] arr) {
+ void testMaximumSubarraySum(long expected, int k, int[] arr) {
assertEquals(expected, MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(k, arr));
}
+ /**
+ * Provides test cases for the parameterized test.
+ *
+ * Test cases cover:
+ * - Normal cases with distinct and duplicate elements
+ * - Edge cases (empty array, k = 0, k > array length)
+ * - Single element arrays
+ * - Arrays with all duplicates
+ * - Negative numbers
+ * - Large sums
+ *
+ * @return stream of test arguments
+ */
private static Stream inputStream() {
- return Stream.of(Arguments.of(15, 3, new int[] {1, 5, 4, 2, 9, 9, 9}), Arguments.of(0, 3, new int[] {4, 4, 4}), Arguments.of(12, 3, new int[] {9, 9, 9, 1, 2, 3}), Arguments.of(0, 0, new int[] {9, 9, 9}), Arguments.of(0, 5, new int[] {9, 9, 9}), Arguments.of(9, 1, new int[] {9, 2, 3, 7}),
- Arguments.of(15, 5, new int[] {1, 2, 3, 4, 5}), Arguments.of(6, 3, new int[] {-1, 2, 3, 1, -2, 4}), Arguments.of(10, 1, new int[] {10}), Arguments.of(0, 2, new int[] {7, 7, 7, 7}), Arguments.of(0, 3, new int[] {}), Arguments.of(0, 10, new int[] {1, 2, 3}));
+ return Stream.of(
+ // Normal case: [5, 4, 2] has distinct elements with sum 11, but [4, 2, 9] also
+ // distinct with sum 15
+ Arguments.of(15L, 3, new int[] {1, 5, 4, 2, 9, 9, 9}),
+ // All elements are same, no distinct subarray of size 3
+ Arguments.of(0L, 3, new int[] {4, 4, 4}),
+ // First three have duplicates, but [1, 2, 3] are distinct with sum 6, wait
+ // [9,1,2] has sum 12
+ Arguments.of(12L, 3, new int[] {9, 9, 9, 1, 2, 3}),
+ // k = 0, should return 0
+ Arguments.of(0L, 0, new int[] {9, 9, 9}),
+ // k > array length, should return 0
+ Arguments.of(0L, 5, new int[] {9, 9, 9}),
+ // k = 1, single element (always distinct)
+ Arguments.of(9L, 1, new int[] {9, 2, 3, 7}),
+ // All distinct elements, size matches array
+ Arguments.of(15L, 5, new int[] {1, 2, 3, 4, 5}),
+ // Array with negative numbers
+ Arguments.of(6L, 3, new int[] {-1, 2, 3, 1, -2, 4}),
+ // Single element array
+ Arguments.of(10L, 1, new int[] {10}),
+ // All duplicates with k = 2
+ Arguments.of(0L, 2, new int[] {7, 7, 7, 7}),
+ // Empty array
+ Arguments.of(0L, 3, new int[] {}),
+ // k much larger than array length
+ Arguments.of(0L, 10, new int[] {1, 2, 3}));
+ }
+
+ /**
+ * Test with a larger array and larger k value.
+ */
+ @Test
+ void testLargerArray() {
+ int[] arr = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(5, arr);
+ // Maximum sum with 5 distinct elements: [6,7,8,9,10] = 40
+ assertEquals(40L, result);
+ }
+
+ /**
+ * Test with negative k value.
+ */
+ @Test
+ void testNegativeK() {
+ int[] arr = new int[] {1, 2, 3, 4, 5};
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(-1, arr);
+ assertEquals(0L, result);
+ }
+
+ /**
+ * Test with null array.
+ */
+ @Test
+ void testNullArray() {
+ int[] nullArray = null;
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, new int[][] {nullArray}[0]);
+ assertEquals(0L, result);
+ }
+
+ /**
+ * Test with array containing duplicates at boundaries.
+ */
+ @Test
+ void testDuplicatesAtBoundaries() {
+ int[] arr = new int[] {1, 1, 2, 3, 4, 4};
+ // [2, 3, 4] is the only valid window with sum 9
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr);
+ assertEquals(9L, result);
+ }
+
+ /**
+ * Test with large numbers to verify long return type.
+ */
+ @Test
+ void testLargeNumbers() {
+ int[] arr = new int[] {1000000, 2000000, 3000000, 4000000};
+ // All elements are distinct, max sum with k=3 is [2000000, 3000000, 4000000] =
+ // 9000000
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr);
+ assertEquals(9000000L, result);
+ }
+
+ /**
+ * Test where multiple windows have the same maximum sum.
+ */
+ @Test
+ void testMultipleMaxWindows() {
+ int[] arr = new int[] {1, 2, 3, 4, 3, 2, 1};
+ // Windows [1,2,3], [2,3,4], [4,3,2], [3,2,1] - max is [2,3,4] = 9
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr);
+ assertEquals(9L, result);
+ }
+
+ /**
+ * Test with only two elements and k=2.
+ */
+ @Test
+ void testTwoElementsDistinct() {
+ int[] arr = new int[] {5, 10};
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(2, arr);
+ assertEquals(15L, result);
+ }
+
+ /**
+ * Test with only two elements (duplicates) and k=2.
+ */
+ @Test
+ void testTwoElementsDuplicate() {
+ int[] arr = new int[] {5, 5};
+ long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(2, arr);
+ assertEquals(0L, result);
}
}