Facebook Pixel

2644. Find the Maximum Divisibility Score

Problem Description

You are given two integer arrays: nums and divisors.

For each element in the divisors array, you need to calculate its divisibility score. The divisibility score of divisors[i] is defined as the count of elements in nums that are divisible by divisors[i].

Your task is to find which element in divisors has the highest divisibility score and return that element. If there's a tie (multiple elements have the same maximum score), you should return the smallest element among them.

For example:

  • If nums = [4, 6, 8] and divisors = [2, 3]
  • For divisors[0] = 2: elements 4, 6, and 8 are all divisible by 2, so score = 3
  • For divisors[1] = 3: only element 6 is divisible by 3, so score = 1
  • Since 2 has the higher score (3 > 1), we return 2

The key points are:

  1. Calculate how many numbers in nums each divisor can divide evenly (remainder is 0)
  2. Find the divisor with the maximum count
  3. If multiple divisors have the same maximum count, choose the smallest divisor value
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The problem asks us to find the "best" divisor based on how many numbers it can divide. This naturally leads us to think about checking each divisor individually and counting its performance.

Since we need to know the divisibility score for every divisor to determine which one is the maximum, we can't avoid checking all of them. This means we need to iterate through each divisor and count how many numbers from nums it can divide.

For each divisor div, we go through all numbers in nums and check if each number is divisible by div using the modulo operation (num % div == 0). If the remainder is 0, the number is divisible.

As we calculate each divisor's score, we need to keep track of:

  1. The maximum score seen so far (mx)
  2. The divisor that achieved this maximum score (ans)

The comparison logic follows naturally from the problem requirements:

  • When we find a divisor with a higher score than our current maximum, it becomes our new best candidate
  • When we find a divisor with the same score as our maximum, we only update if this divisor is smaller (since the problem asks for the smallest divisor in case of ties)

This straightforward enumeration approach works well because we must examine every divisor anyway to ensure we find the one with the maximum score. There's no way to skip checking any divisor since any of them could potentially have the highest score.

Solution Approach

The solution uses a simple enumeration approach to find the divisor with the maximum divisibility score.

We initialize two variables:

  • ans = divisors[0]: Stores the divisor with the maximum score (initially set to the first divisor)
  • mx = 0: Stores the current maximum divisibility score

For each divisor div in the divisors array, we:

  1. Calculate the divisibility score: Count how many elements in nums are divisible by div

    • This is done using sum(x % div == 0 for x in nums)
    • The expression x % div == 0 returns True (counted as 1) if x is divisible by div, otherwise False (counted as 0)
    • The sum() function adds up all the True values to get the total count
  2. Update the maximum based on the score:

    • If cnt > mx: We found a better divisor with a higher score
      • Update both mx = cnt and ans = div
    • If cnt == mx and div < ans: We found a divisor with the same score but smaller value
      • Update ans = div to keep the smallest divisor with the maximum score
  3. Return the result: After checking all divisors, ans contains the divisor with the maximum score (and the smallest value in case of ties)

The time complexity is O(n × m) where n is the length of nums and m is the length of divisors, since for each divisor we check all numbers. The space complexity is O(1) as we only use a constant amount of extra space for variables.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with nums = [12, 6, 15, 9] and divisors = [3, 6, 2].

We start by initializing:

  • ans = divisors[0] = 3 (our current best divisor)
  • mx = 0 (the maximum score seen so far)

Step 1: Check divisor = 3

  • Count elements in nums divisible by 3:
    • 12 % 3 = 0 ✓ (divisible)
    • 6 % 3 = 0 ✓ (divisible)
    • 15 % 3 = 0 ✓ (divisible)
    • 9 % 3 = 0 ✓ (divisible)
  • Score = 4
  • Since 4 > 0 (our current max), update: mx = 4, ans = 3

Step 2: Check divisor = 6

  • Count elements in nums divisible by 6:
    • 12 % 6 = 0 ✓ (divisible)
    • 6 % 6 = 0 ✓ (divisible)
    • 15 % 6 = 3 ✗ (not divisible)
    • 9 % 6 = 3 ✗ (not divisible)
  • Score = 2
  • Since 2 < 4 (our current max), no update needed

Step 3: Check divisor = 2

  • Count elements in nums divisible by 2:
    • 12 % 2 = 0 ✓ (divisible)
    • 6 % 2 = 0 ✓ (divisible)
    • 15 % 2 = 1 ✗ (not divisible)
    • 9 % 2 = 1 ✗ (not divisible)
  • Score = 2
  • Since 2 < 4 (our current max), no update needed

Result: Return ans = 3 as it has the highest divisibility score of 4.

Note how if divisor 2 had also scored 4, we would have updated ans = 2 since 2 < 3 (choosing the smaller divisor in case of a tie).

Solution Implementation

1from typing import List
2
3class Solution:
4    def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
5        """
6        Find the divisor that has the maximum divisibility score.
7        The divisibility score is the count of elements in nums that are divisible by the divisor.
8        If multiple divisors have the same score, return the smallest one.
9      
10        Args:
11            nums: List of integers to check divisibility
12            divisors: List of candidate divisors
13          
14        Returns:
15            The divisor with the maximum divisibility score
16        """
17        # Initialize with the first divisor and score of 0
18        best_divisor = divisors[0]
19        max_score = 0
20      
21        # Check each divisor
22        for divisor in divisors:
23            # Count how many numbers in nums are divisible by current divisor
24            current_score = sum(num % divisor == 0 for num in nums)
25          
26            # Update if we found a better score
27            if max_score < current_score:
28                max_score = current_score
29                best_divisor = divisor
30            # If same score, choose the smaller divisor
31            elif max_score == current_score and best_divisor > divisor:
32                best_divisor = divisor
33      
34        return best_divisor
35
1class Solution {
2    public int maxDivScore(int[] nums, int[] divisors) {
3        // Initialize result with the first divisor
4        int result = divisors[0];
5        // Track the maximum count of divisible numbers found so far
6        int maxCount = 0;
7      
8        // Iterate through each divisor to find the one with maximum divisibility score
9        for (int divisor : divisors) {
10            // Count how many numbers in nums are divisible by current divisor
11            int currentCount = 0;
12            for (int number : nums) {
13                if (number % divisor == 0) {
14                    currentCount++;
15                }
16            }
17          
18            // Update result if we found a better divisor (higher count)
19            if (currentCount > maxCount) {
20                maxCount = currentCount;
21                result = divisor;
22            } 
23            // If count is the same, choose the smaller divisor
24            else if (currentCount == maxCount) {
25                result = Math.min(result, divisor);
26            }
27        }
28      
29        return result;
30    }
31}
32
1class Solution {
2public:
3    int maxDivScore(vector<int>& nums, vector<int>& divisors) {
4        // Initialize the result with the first divisor
5        int result = divisors[0];
6        // Track the maximum count of divisible numbers found so far
7        int maxCount = 0;
8      
9        // Iterate through each divisor to find the one with the highest score
10        for (int currentDivisor : divisors) {
11            // Count how many numbers in nums are divisible by the current divisor
12            int divisibleCount = 0;
13            for (int number : nums) {
14                // Increment count if the number is divisible by current divisor
15                divisibleCount += (number % currentDivisor == 0);
16            }
17          
18            // Update result if we found a higher score
19            if (maxCount < divisibleCount) {
20                maxCount = divisibleCount;
21                result = currentDivisor;
22            } 
23            // If score is the same, choose the smaller divisor
24            else if (maxCount == divisibleCount) {
25                result = min(result, currentDivisor);
26            }
27        }
28      
29        return result;
30    }
31};
32
1/**
2 * Finds the divisor with the maximum divisibility score.
3 * The divisibility score is the count of numbers in nums that are divisible by the divisor.
4 * If multiple divisors have the same score, returns the smallest divisor.
5 * 
6 * @param nums - Array of numbers to check divisibility against
7 * @param divisors - Array of potential divisors to evaluate
8 * @returns The divisor with the maximum divisibility score
9 */
10function maxDivScore(nums: number[], divisors: number[]): number {
11    // Initialize result with the first divisor
12    let result: number = divisors[0];
13  
14    // Track the maximum divisibility score found so far
15    let maxScore: number = 0;
16  
17    // Iterate through each divisor to calculate its divisibility score
18    for (const divisor of divisors) {
19        // Count how many numbers in nums are divisible by current divisor
20        const divisibilityCount: number = nums.reduce(
21            (accumulator: number, currentNum: number) => 
22                accumulator + (currentNum % divisor === 0 ? 1 : 0), 
23            0
24        );
25      
26        // Update result if we found a better score
27        if (maxScore < divisibilityCount) {
28            maxScore = divisibilityCount;
29            result = divisor;
30        } 
31        // If same score, choose the smaller divisor
32        else if (maxScore === divisibilityCount && result > divisor) {
33            result = divisor;
34        }
35    }
36  
37    return result;
38}
39

Time and Space Complexity

The time complexity is O(m × n), where m is the length of divisors and n is the length of nums. This is because the code iterates through each divisor in divisors (outer loop with m iterations), and for each divisor, it iterates through all elements in nums to count how many are divisible by that divisor (inner iteration with n elements via the sum function with generator expression). Therefore, the total number of modulo operations performed is m × n.

The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables ans, mx, div, and cnt, regardless of the input size. The generator expression (x % div == 0 for x in nums) doesn't create an intermediate list but generates values on-the-fly, so it doesn't consume additional space proportional to the input size.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Division by Zero Error

The Problem: If any element in the divisors array is 0, the modulo operation num % divisor will raise a ZeroDivisionError. This is a critical runtime error that will crash the program.

Example of the issue:

nums = [4, 6, 8]
divisors = [2, 0, 3]  # Contains 0
# When checking divisor = 0: num % 0 raises ZeroDivisionError

Solution: Add a check to handle zero divisors safely:

def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
    best_divisor = divisors[0]
    max_score = 0
  
    for divisor in divisors:
        if divisor == 0:
            # Handle zero divisor - skip it or treat specially
            # Option 1: Skip zero divisors
            continue
            # Option 2: Treat 0 as having score 0
            # current_score = 0
        else:
            current_score = sum(num % divisor == 0 for num in nums)
      
        if max_score < current_score:
            max_score = current_score
            best_divisor = divisor
        elif max_score == current_score and best_divisor > divisor:
            best_divisor = divisor
  
    return best_divisor

2. Incorrect Tie-Breaking Logic

The Problem: The original code has a subtle bug in the tie-breaking condition. It uses best_divisor > divisor instead of divisor < best_divisor. While mathematically equivalent, the condition max_score == current_score and best_divisor > divisor can be confusing and prone to logical errors during maintenance.

More importantly, if the first divisor happens to be the answer (has the maximum score and is the smallest among ties), the initialization might cause issues if not handled properly.

Solution: Use clearer logic and ensure proper initialization:

def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
    best_divisor = divisors[0]
    max_score = -1  # Initialize to -1 to ensure first divisor is properly evaluated
  
    for divisor in divisors:
        current_score = sum(num % divisor == 0 for num in nums)
      
        # Clearer tie-breaking logic
        if current_score > max_score or (current_score == max_score and divisor < best_divisor):
            max_score = current_score
            best_divisor = divisor
  
    return best_divisor

3. Empty Arrays Not Handled

The Problem: The code assumes both nums and divisors are non-empty. If divisors is empty, divisors[0] will raise an IndexError.

Solution: Add validation for edge cases:

def maxDivScore(self, nums: List[int], divisors: List[int]) -> int:
    if not divisors:
        raise ValueError("divisors array cannot be empty")
  
    if not nums:
        # All divisors have score 0, return the smallest divisor
        return min(divisors)
  
    # Rest of the implementation...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More