LeetCode 2644 - Find the Maximum Divisibility Score
The problem asks us to identify, from a list of divisors, the integer that has the maximum divisibility score with respect to a given array of numbers. The divisibility score of a divisor is defined as the count of elements in nums that are divisible by that divisor.
Difficulty: 🟢 Easy
Topics: Array
Solution
Problem Understanding
The problem asks us to identify, from a list of divisors, the integer that has the maximum divisibility score with respect to a given array of numbers. The divisibility score of a divisor is defined as the count of elements in nums that are divisible by that divisor. In simpler terms, for each divisor, we count how many numbers in the array nums can be divided evenly by it. Among all divisors, we want the one with the highest count, and if there is a tie, we return the smallest divisor.
The input consists of two integer arrays: nums and divisors. Each integer can be up to 10^9, but the lengths of the arrays are capped at 1000. The constraints are small enough that a brute-force solution of checking each number in nums against every divisor is feasible, though an optimized solution is straightforward.
Key edge cases include divisors with no matches in nums, multiple divisors with the same maximum score, and scenarios where all numbers in nums are divisible by one divisor. The problem guarantees non-empty arrays and positive integers, so we do not need to handle zeros or empty inputs.
Approaches
The brute-force approach iterates through each divisor and checks every number in nums to see if it is divisible. This is correct because it directly computes the divisibility score for every divisor. However, this requires O(n * m) operations, where n is the length of nums and m is the length of divisors. Given the constraints (both up to 1000), this results in at most 1,000,000 checks, which is acceptable for this problem.
The optimal approach is essentially the same as brute force because the problem size is small. There is no need for sophisticated techniques like sieving or hashing, as a direct double iteration suffices. The key is to maintain both the maximum score and the corresponding smallest divisor while iterating, ensuring ties are handled correctly.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * m) | O(1) | Check each divisor against all numbers in nums, track maximum score |
| Optimal | O(n * m) | O(1) | Same as brute force, but with tie-breaking logic for smallest divisor |
Algorithm Walkthrough
- Initialize two variables:
max_scoreto zero to track the highest divisibility score found so far, andbest_divisorto a large number to ensure the first comparison updates it. - Iterate over each divisor in
divisors. - For the current divisor, initialize a counter to zero.
- Iterate over each number in
numsand increment the counter if the number is divisible by the current divisor. - After counting, compare the counter with
max_score. If it is higher, updatemax_scoreand setbest_divisorto the current divisor. If it is equal, updatebest_divisoronly if the current divisor is smaller than the existingbest_divisor. - Continue this process until all divisors are evaluated.
- Return
best_divisoras the result.
Why it works: At every step, we maintain the maximum divisibility score and the smallest divisor achieving it. This invariant ensures that after processing all divisors, best_divisor contains the correct value according to the problem's requirements.
Python Solution
from typing import List
class Solution:
def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
max_score = 0
best_divisor = float('inf')
for divisor in divisors:
count = 0
for num in nums:
if num % divisor == 0:
count += 1
if count > max_score or (count == max_score and divisor < best_divisor):
max_score = count
best_divisor = divisor
return best_divisor
Implementation walkthrough: We start by initializing max_score and best_divisor. For each divisor, we count how many numbers it divides evenly. If this count beats our current maximum, we update both max_score and best_divisor. If there is a tie, we only update best_divisor if the new divisor is smaller, ensuring the correct tie-breaking behavior.
Go Solution
func maxDivScore(nums []int, divisors []int) int {
maxScore := 0
bestDivisor := int(1e9 + 1) // initialize larger than any possible divisor
for _, divisor := range divisors {
count := 0
for _, num := range nums {
if num % divisor == 0 {
count++
}
}
if count > maxScore || (count == maxScore && divisor < bestDivisor) {
maxScore = count
bestDivisor = divisor
}
}
return bestDivisor
}
Go-specific notes: We use 1e9 + 1 to initialize bestDivisor to ensure the first divisor updates it. Go does not require explicit type hints for slices, and we handle all numbers as int since constraints are within safe ranges.
Worked Examples
Example 1: nums = [2,9,15,50], divisors = [5,3,7,2]
| Divisor | Count | Max? | Best Divisor |
|---|---|---|---|
| 5 | 2 | Yes | 5 |
| 3 | 2 | Tie | 3 (update to smaller: 3) |
| 7 | 0 | No | 3 |
| 2 | 2 | Tie | 2 (update to smaller: 2) |
Result: 2
Example 2: nums = [4,7,9,3,9], divisors = [5,2,3]
| Divisor | Count | Max? | Best Divisor |
|---|---|---|---|
| 5 | 0 | No | 0 |
| 2 | 1 | Yes | 2 |
| 3 | 3 | Yes | 3 |
Result: 3
Example 3: nums = [20,14,21,10], divisors = [10,16,20]
| Divisor | Count | Max? | Best Divisor |
|---|---|---|---|
| 10 | 2 | Yes | 10 |
| 16 | 0 | No | 10 |
| 20 | 1 | No | 10 |
Result: 10
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * m) | Each divisor is checked against every number in nums, resulting in n*m operations |
| Space | O(1) | Only a few integer variables are used; no additional data structures needed |
Given n, m ≤ 1000, the maximum of 1,000,000 operations is efficiently handled.
Test Cases
# Provided examples
assert Solution().maxDivScore([2,9,15,50], [5,3,7,2]) == 2 # multiple ties, smallest divisor returned
assert Solution().maxDivScore([4,7,9,3,9], [5,2,3]) == 3 # clear winner
assert Solution().maxDivScore([20,14,21,10], [10,16,20]) == 10 # max divisor is first
# Edge cases
assert Solution().maxDivScore([1,1,1,1], [1]) == 1 # all nums divisible by 1
assert Solution().maxDivScore([1,2,3,4,5], [10,20,30]) == 10 # no divisors match, return first smallest
assert Solution().maxDivScore([1000000000], [1,2,1000000000]) == 1 # large numbers
| Test | Why |
|---|---|
| [2,9,15,50], [5,3,7,2] | Tests tie-breaking among divisors |
| [4,7,9,3,9], [5,2,3] | Tests clear winner |
| [20,14,21,10], [10,16,20] | Tests selection when first divisor has max score |
| [1,1,1,1], [1] | Tests all numbers divisible by divisor |
| [1,2,3,4,5], [10,20,30] | Tests no divisors match |
| [1000000000], [1,2,1000000000] | Tests large numbers |
Edge Cases
One important edge case is when all divisors have a score of zero, meaning no number in nums is divisible by any divisor. The implementation correctly returns the smallest divisor due to the tie-breaking logic, which is triggered when counts are equal (including zero).
Another edge case is when multiple divisors share the maximum score. Without careful handling, a naive implementation might return the last encountered divisor. By explicitly checking for divisor < best_divisor when scores tie, we ensure the smallest one is chosen.
A third edge case involves very large numbers, approaching 10^9. Python handles large integers natively, so there is no risk of overflow. In Go, the solution uses int, which can safely hold