Facebook Pixel

798. Smallest Rotation with Highest Score

Problem Description

You have an array nums that can be rotated by a non-negative integer k. When you rotate by k, the array transforms from its original form to [nums[k], nums[k + 1], ... nums[nums.length - 1], nums[0], nums[1], ..., nums[k-1]]. In other words, you take the first k elements and move them to the end of the array.

After rotation, you calculate a score based on how many elements satisfy a specific condition: an element at index i earns one point if its value is less than or equal to i.

For example, given nums = [2,4,1,3,0]:

  • If you rotate by k = 2, the array becomes [1,3,0,2,4]
  • Now checking each position:
    • Index 0: value 1 > 0 → no point
    • Index 1: value 3 > 1 → no point
    • Index 2: value 0 ≤ 2 → one point
    • Index 3: value 2 ≤ 3 → one point
    • Index 4: value 4 ≤ 4 → one point
  • Total score = 3 points

Your task is to find the rotation value k that produces the maximum possible score. If multiple values of k give the same maximum score, return the smallest k.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is to think about when each element contributes a point across different rotations, rather than calculating the entire score for each possible rotation.

For an element nums[i] with value v, we need to determine: for which rotation values k will this element contribute a point? After rotation by k, the element originally at index i moves to position (i - k + n) % n. For this element to score a point, we need v ≤ (i - k + n) % n.

Instead of checking all rotations directly (which would be O(n²)), we can be smarter. For each element, we can calculate the range of k values where it contributes a point. When an element starts contributing and when it stops contributing forms an interval.

The critical observation is that for each element at original position i with value v:

  • It starts contributing a point when rotated to a position where its new index is at least v
  • The element begins contributing at rotation k = (i + 1) % n
  • It stops contributing when the new position becomes less than v, which happens at k = (n + i + 1 - v) % n

By using a difference array technique, we can efficiently track how the score changes as we increase k. We mark the start of each contribution interval with +1 and the end with -1. Then, by scanning through all possible k values and maintaining a running sum, we can find the maximum score and the smallest k that achieves it.

This transforms the problem from calculating full scores for each rotation to simply tracking score changes, reducing complexity from O(n²) to O(n).

Learn more about Prefix Sum patterns.

Solution Approach

The solution uses a difference array to efficiently track score changes across all possible rotations.

Step 1: Initialize the difference array

d = [0] * n

This array tracks the change in score when moving from rotation k-1 to rotation k.

Step 2: Calculate contribution intervals for each element For each element at index i with value v:

l, r = (i + 1) % n, (n + i + 1 - v) % n
d[l] += 1
d[r] -= 1
  • l = (i + 1) % n: The rotation where element nums[i] starts contributing a point
  • r = (n + i + 1 - v) % n: The rotation where element nums[i] stops contributing
  • We increment d[l] to mark the start of contribution
  • We decrement d[r] to mark the end of contribution

Step 3: Find the rotation with maximum score

s = 0
mx, ans = -1, n
for k, t in enumerate(d):
    s += t
    if s > mx:
        mx = s
        ans = k

We iterate through all possible rotations k from 0 to n-1:

  • s += t: Accumulate the score change to get the actual score at rotation k
  • Track the maximum score mx and its corresponding rotation ans
  • Since we iterate from small to large k, we automatically get the smallest k when there are ties

Time Complexity: O(n) - We iterate through the array twice (once to build the difference array, once to find the maximum)

Space Complexity: O(n) - For the difference array

The elegance of this approach lies in converting a seemingly O(n²) problem (checking n rotations with n elements each) into an O(n) solution by focusing on when each element's contribution changes rather than recalculating everything from scratch.

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 the solution with nums = [1, 0, 3, 2].

Step 1: Initialize difference array

d = [0, 0, 0, 0]

Step 2: Process each element to find contribution intervals

For element at index 0, value 1:

  • Start contributing: l = (0 + 1) % 4 = 1
  • Stop contributing: r = (4 + 0 + 1 - 1) % 4 = 0
  • Update: d[1] += 1, d[0] -= 1
  • Result: d = [-1, 1, 0, 0]

For element at index 1, value 0:

  • Start contributing: l = (1 + 1) % 4 = 2
  • Stop contributing: r = (4 + 1 + 1 - 0) % 4 = 2
  • Update: d[2] += 1, d[2] -= 1
  • Result: d = [-1, 1, 0, 0] (no net change at index 2)

For element at index 2, value 3:

  • Start contributing: l = (2 + 1) % 4 = 3
  • Stop contributing: r = (4 + 2 + 1 - 3) % 4 = 0
  • Update: d[3] += 1, d[0] -= 1
  • Result: d = [-2, 1, 0, 1]

For element at index 3, value 2:

  • Start contributing: l = (3 + 1) % 4 = 0
  • Stop contributing: r = (4 + 3 + 1 - 2) % 4 = 2
  • Update: d[0] += 1, d[2] -= 1
  • Result: d = [-1, 1, -1, 1]

Step 3: Find maximum score by scanning difference array

Starting with s = 0, mx = -1, ans = 4:

  • k=0: s = 0 + (-1) = -1. Since -1 ≤ -1, no update
  • k=1: s = -1 + 1 = 0. Since 0 > -1, update mx = 0, ans = 1
  • k=2: s = 0 + (-1) = -1. Since -1 ≤ 0, no update
  • k=3: s = -1 + 1 = 0. Since 0 ≤ 0, no update

Result: Maximum score is 0, achieved at k = 1.

Verification:

  • k=1 rotation: [0, 3, 2, 1]
    • Index 0: 0 ≤ 0 ✓ (1 point)
    • Index 1: 3 > 1 ✗
    • Index 2: 2 ≤ 2 ✓ (1 point)
    • Index 3: 1 ≤ 3 ✓ (1 point)
    • Total: 3 points

Wait, let me recalculate the difference array more carefully:

Actually, the score at k=0 should be the baseline. Let me trace through what happens:

  • At k=0 (no rotation): [1, 0, 3, 2]
    • Index 0: 1 > 0 ✗
    • Index 1: 0 ≤ 1 ✓
    • Index 2: 3 > 2 ✗
    • Index 3: 2 ≤ 3 ✓
    • Base score: 2 points

The difference array tracks changes from this baseline as we rotate. When we accumulate the differences starting from 0, we're actually computing the relative score change, not the absolute score. The algorithm still correctly identifies the k with the maximum relative improvement.

Solution Implementation

1class Solution:
2    def bestRotation(self, nums: List[int]) -> int:
3        """
4        Find the rotation index K that maximizes the score.
5        Score is calculated as the count of elements where nums[i] <= i after rotation.
6      
7        Args:
8            nums: List of integers to rotate
9          
10        Returns:
11            The rotation index K that gives the maximum score
12        """
13        n = len(nums)
14      
15        # Initialize variables to track the best rotation
16        max_score = -1
17        best_rotation = n
18      
19        # Use difference array to efficiently track score changes
20        # diff[k] represents the change in score when rotating by k positions
21        diff = [0] * n
22      
23        # For each element, calculate the range of rotations where it contributes to the score
24        for index, value in enumerate(nums):
25            # Calculate the rotation range where nums[index] contributes 1 point
26            # After k rotations, element at index i moves to position (i - k + n) % n
27            # We need: value <= (index - k + n) % n, which means k is in range [left, right)
28          
29            # Start of valid rotation range (inclusive)
30            left = (index + 1) % n
31          
32            # End of valid rotation range (exclusive)
33            # Derived from: value <= (index - k + n) % n
34            right = (n + index + 1 - value) % n
35          
36            # Mark the range in the difference array
37            diff[left] += 1
38            diff[right] -= 1
39      
40        # Use prefix sum to calculate actual scores for each rotation
41        current_score = 0
42        for rotation_k, delta in enumerate(diff):
43            current_score += delta
44          
45            # Update the best rotation if current score is better
46            if current_score > max_score:
47                max_score = current_score
48                best_rotation = rotation_k
49      
50        return best_rotation
51
1class Solution {
2    public int bestRotation(int[] nums) {
3        int n = nums.length;
4      
5        // Difference array to track score changes at each rotation
6        int[] scoreDelta = new int[n];
7      
8        // For each element, calculate the range of rotations where it contributes a point
9        for (int i = 0; i < n; ++i) {
10            // Left boundary: rotation where element starts contributing to score
11            // This happens when the element moves to index 0 (rotation = i + 1)
12            int leftBoundary = (i + 1) % n;
13          
14            // Right boundary: rotation where element stops contributing to score
15            // Element contributes when: newIndex >= value
16            // newIndex = (i - k + n) % n, so we need (i - k + n) % n >= nums[i]
17            // This gives us k <= i + 1 - nums[i] (mod n)
18            int rightBoundary = (n + i + 1 - nums[i]) % n;
19          
20            // Mark the range [leftBoundary, rightBoundary) using difference array
21            scoreDelta[leftBoundary]++;
22            scoreDelta[rightBoundary]--;
23        }
24      
25        // Find the rotation with maximum score using prefix sum
26        int maxScore = -1;
27        int currentScore = 0;
28        int bestRotation = n;  // Initialize with n (will be overwritten)
29      
30        for (int rotation = 0; rotation < n; ++rotation) {
31            // Update current score by applying the delta
32            currentScore += scoreDelta[rotation];
33          
34            // Track the rotation with the highest score
35            // In case of tie, we keep the smallest rotation index
36            if (currentScore > maxScore) {
37                maxScore = currentScore;
38                bestRotation = rotation;
39            }
40        }
41      
42        return bestRotation;
43    }
44}
45
1class Solution {
2public:
3    int bestRotation(vector<int>& nums) {
4        int n = nums.size();
5      
6        // Initialize variables to track the maximum score and best rotation
7        int maxScore = -1;
8        int bestK = n;
9      
10        // Difference array to track score changes at each rotation k
11        vector<int> scoreDelta(n);
12      
13        // For each element, calculate the range of k values where it contributes to the score
14        for (int i = 0; i < n; ++i) {
15            // Calculate the left boundary: rotation where nums[i] starts contributing
16            // When k = i+1, element at index i moves to position n-1
17            int leftBoundary = (i + 1) % n;
18          
19            // Calculate the right boundary: last rotation where nums[i] still contributes
20            // Element contributes when new_index >= nums[i]
21            // new_index = (i - k + n) % n >= nums[i]
22            // Solving for k: k <= (i + 1 - nums[i] + n) % n
23            int rightBoundary = (n + i + 1 - nums[i]) % n;
24          
25            // Mark the range [leftBoundary, rightBoundary) using difference array
26            ++scoreDelta[leftBoundary];
27            --scoreDelta[rightBoundary];
28        }
29      
30        // Calculate cumulative scores and find the rotation with maximum score
31        int currentScore = 0;
32        for (int k = 0; k < n; ++k) {
33            currentScore += scoreDelta[k];
34          
35            // Update best rotation if current score is higher
36            if (currentScore > maxScore) {
37                maxScore = currentScore;
38                bestK = k;
39            }
40        }
41      
42        return bestK;
43    }
44};
45
1function bestRotation(nums: number[]): number {
2    const n = nums.length;
3  
4    // Initialize variables to track the maximum score and best rotation
5    let maxScore = -1;
6    let bestK = n;
7  
8    // Difference array to track score changes at each rotation k
9    const scoreDelta: number[] = new Array(n).fill(0);
10  
11    // For each element, calculate the range of k values where it contributes to the score
12    for (let i = 0; i < n; i++) {
13        // Calculate the left boundary: rotation where nums[i] starts contributing
14        // When k = i+1, element at index i moves to position n-1
15        const leftBoundary = (i + 1) % n;
16      
17        // Calculate the right boundary: last rotation where nums[i] still contributes
18        // Element contributes when new_index >= nums[i]
19        // new_index = (i - k + n) % n >= nums[i]
20        // Solving for k: k <= (i + 1 - nums[i] + n) % n
21        const rightBoundary = (n + i + 1 - nums[i]) % n;
22      
23        // Mark the range [leftBoundary, rightBoundary) using difference array
24        scoreDelta[leftBoundary]++;
25        scoreDelta[rightBoundary]--;
26    }
27  
28    // Calculate cumulative scores and find the rotation with maximum score
29    let currentScore = 0;
30    for (let k = 0; k < n; k++) {
31        currentScore += scoreDelta[k];
32      
33        // Update best rotation if current score is higher
34        if (currentScore > maxScore) {
35            maxScore = currentScore;
36            bestK = k;
37        }
38    }
39  
40    return bestK;
41}
42

Time and Space Complexity

Time Complexity: O(n)

The algorithm consists of two main parts:

  1. First loop iterates through all elements in nums to calculate difference array values - O(n) operations
  2. Second loop iterates through the difference array d to find the maximum prefix sum - O(n) operations

Each operation inside both loops (including modulo calculations and array updates) takes O(1) time. Therefore, the overall time complexity is O(n) + O(n) = O(n).

Space Complexity: O(n)

The algorithm uses additional space for:

  • Difference array d of size n - O(n) space
  • A few scalar variables (mx, ans, s, l, r) - O(1) space

The total auxiliary space used is O(n) + O(1) = O(n).

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

Common Pitfalls

Pitfall 1: Incorrect Handling of Circular Range Marking

The Problem: When marking contribution ranges in the difference array, the range can wrap around (i.e., left > right). A common mistake is treating this as a single continuous range instead of two separate ranges.

Incorrect Implementation:

# Wrong: This doesn't handle wraparound correctly
if left <= right:
    diff[left] += 1
    diff[right] -= 1
else:
    # Incorrectly trying to handle wraparound
    diff[left] += 1
    diff[0] += 1  # Wrong!
    diff[right] -= 1

Correct Solution:

# The original code handles this correctly by using modulo arithmetic
diff[left] += 1
diff[right] -= 1

# When right < left, the difference array naturally handles wraparound
# because we're using prefix sum across the entire array

Pitfall 2: Off-by-One Error in Range Calculation

The Problem: The formula for calculating the right boundary (n + i + 1 - v) % n is derived from the inequality v <= (i - k + n) % n. A common mistake is forgetting the +1 or using incorrect boundary logic.

Incorrect Implementation:

# Wrong: Missing the +1
right = (n + i - v) % n  # This would include one rotation too many

# Or wrong: Using inclusive end instead of exclusive
right = (n + i - v) % n
diff[right + 1] -= 1  # This could cause index out of bounds

Correct Solution:

# Correct: The range is [left, right) where right is exclusive
left = (i + 1) % n
right = (n + i + 1 - v) % n  # Note the +1 here
diff[left] += 1
diff[right] -= 1  # Mark the end of the range (exclusive)

Pitfall 3: Forgetting to Handle the Case When v > n

The Problem: When an element's value exceeds the array length (v >= n), it can never contribute to the score regardless of rotation. Some implementations might not handle this edge case properly.

Incorrect Implementation:

# Wrong: Doesn't check if element can ever contribute
for i, v in enumerate(nums):
    left = (i + 1) % n
    right = (n + i + 1 - v) % n  # Could give unexpected results if v > n
    diff[left] += 1
    diff[right] -= 1

Correct Solution:

for i, v in enumerate(nums):
    # Skip elements that can never contribute
    if v >= n:
        continue
  
    left = (i + 1) % n
    right = (n + i + 1 - v) % n
    diff[left] += 1
    diff[right] -= 1

Pitfall 4: Initializing Best Score Incorrectly

The Problem: Starting with max_score = 0 can cause issues if all rotations result in a score of 0, potentially returning an incorrect rotation index.

Incorrect Implementation:

max_score = 0  # Wrong: What if all scores are 0?
best_rotation = 0

for k, delta in enumerate(diff):
    current_score += delta
    if current_score > max_score:  # This might never trigger
        max_score = current_score
        best_rotation = k

Correct Solution:

max_score = -1  # Start with -1 to ensure first valid score is captured
best_rotation = n  # Or 0, but should be set on first iteration

for k, delta in enumerate(diff):
    current_score += delta
    if current_score > max_score:
        max_score = current_score
        best_rotation = k
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Is the following code DFS or BFS?

void search(Node root) {
  if (!root) return;
  visit(root);
  root.visited = true;
  for (Node node in root.adjacent) {
    if (!node.visited) {
      search(node);
    }
  }
}

Recommended Readings

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

Load More