LeetCode 3400 - Maximum Number of Matching Indices After Right Shifts
The problem gives us two arrays, nums1 and nums2, both of the same length n. We are allowed to repeatedly perform a right circular shift on nums1. A right shift moves every element one position to the right, and the last element wraps around to the beginning.
Difficulty: 🟡 Medium
Topics: Array, Two Pointers, Simulation
Solution
Problem Understanding
The problem gives us two arrays, nums1 and nums2, both of the same length n. We are allowed to repeatedly perform a right circular shift on nums1. A right shift moves every element one position to the right, and the last element wraps around to the beginning.
For example:
[1, 2, 3, 4]- after one right shift becomes
[4, 1, 2, 3] - after two right shifts becomes
[3, 4, 1, 2]
After applying any number of right shifts, we want to maximize the number of indices i where:
nums1[i] == nums2[i]
These are called matching indices.
The task is to compute the largest possible number of matching positions over all possible circular right shifts of nums1.
The constraints are important:
1 <= n <= 3000- Array values can be as large as
10^9 - Arrays may contain duplicates
The array size of 3000 is small enough that an O(n^2) solution is acceptable, since 3000^2 = 9,000,000, which is manageable in most programming languages. However, cubic solutions would be too slow.
There are several important edge cases to consider:
- Arrays may already be identical, so zero shifts could be optimal.
- Arrays may have no common values at all, resulting in zero matches regardless of shifts.
- Arrays can contain many duplicate values, meaning multiple shifts may produce the same number of matches.
- Arrays of length 1 must always return either 1 or 0 depending on equality.
- Since shifts are circular, we must handle wraparound carefully using modulo arithmetic.
Approaches
Brute Force Approach
The most direct approach is to simulate every possible right shift of nums1.
There are exactly n distinct circular shifts. For each shift:
- Construct the shifted array.
- Compare it against
nums2. - Count how many indices match.
- Track the maximum count.
This works because trying every possible shift guarantees we eventually examine the optimal alignment.
However, constructing a shifted array for every shift introduces unnecessary work. If we explicitly rebuild the shifted array each time, the total complexity becomes:
O(n)shifts- each requiring
O(n)construction - plus
O(n)comparison
This can become O(n^2) or worse depending on implementation details.
Although O(n^2) is acceptable for this problem size, we can improve the implementation by avoiding explicit shifting.
Key Insight
Instead of physically shifting the array, we can determine directly which element from the original array would appear at each position after a given number of right shifts.
If we perform shift right shifts, then:
shifted[i] = nums1[(i - shift + n) % n]
So for every possible shift:
- iterate through all indices
- compare the implied shifted element with
nums2[i] - count matches
This avoids creating temporary arrays and keeps the implementation clean and efficient.
The resulting complexity is still O(n^2), but with minimal overhead and straightforward logic.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(n) | Explicitly builds shifted arrays |
| Optimal | O(n²) | O(1) | Uses index arithmetic instead of constructing shifts |
Algorithm Walkthrough
- Let
nbe the length of the arrays.
We need to examine every possible circular right shift from 0 to n - 1.
2. Initialize a variable max_matches = 0.
This stores the best answer found so far. 3. For each possible shift value:
A shift value k means every element moves k positions to the right.
4. For the current shift, iterate through every index i.
Instead of actually shifting the array, compute which original element lands at index i.
5. Use circular indexing.
After k right shifts:
shifted[i] = nums1[(i - k + n) % n]
The modulo operation handles wraparound correctly.
6. Compare the shifted value with nums2[i].
If they are equal, increment the current match counter. 7. After checking all indices for the current shift:
Update:
max_matches = max(max_matches, current_matches)
- After all shifts are processed, return
max_matches.
Why it works
A circular right shift only changes where elements appear, not their relative cyclic order. For any shift value k, the formula:
nums1[(i - k + n) % n]
correctly identifies which original element moves into position i.
Since the algorithm checks every possible shift and counts exact matches for each one, the maximum recorded value must be the optimal answer.
Python Solution
from typing import List
class Solution:
def maximumMatchingIndices(self, nums1: List[int], nums2: List[int]) -> int:
n = len(nums1)
max_matches = 0
for shift in range(n):
current_matches = 0
for i in range(n):
shifted_value = nums1[(i - shift + n) % n]
if shifted_value == nums2[i]:
current_matches += 1
max_matches = max(max_matches, current_matches)
return max_matches
The implementation begins by storing the array length in n. Since there are exactly n distinct circular shifts, the outer loop iterates through every shift value from 0 to n - 1.
For each shift, the algorithm counts how many positions match. Instead of creating a shifted copy of nums1, it computes the shifted element directly using modular arithmetic.
The expression:
(i - shift + n) % n
maps the current index in the shifted array back to the correct index in the original array. Adding n ensures the value stays non-negative before applying modulo.
Whenever the shifted value equals nums2[i], the match counter increases. After processing all indices for a shift, the algorithm updates the global maximum.
The final answer is the largest match count encountered across all shifts.
Go Solution
func maximumMatchingIndices(nums1 []int, nums2 []int) int {
n := len(nums1)
maxMatches := 0
for shift := 0; shift < n; shift++ {
currentMatches := 0
for i := 0; i < n; i++ {
shiftedValue := nums1[(i-shift+n)%n]
if shiftedValue == nums2[i] {
currentMatches++
}
}
if currentMatches > maxMatches {
maxMatches = currentMatches
}
}
return maxMatches
}
The Go implementation follows the same logic as the Python version. Go arrays are handled as slices, so indexing works naturally with modular arithmetic.
One small difference is that Go does not provide a built-in max function for integers, so the code uses a simple conditional comparison instead.
Integer overflow is not a concern because the indices and counts remain within the bounds of the array size, which is at most 3000.
Worked Examples
Example 1
nums1 = [3,1,2,3,1,2]
nums2 = [1,2,3,1,2,3]
Length:
n = 6
We test every shift.
| Shift | Shifted nums1 | Matching Indices | Match Count |
|---|---|---|---|
| 0 | [3,1,2,3,1,2] | none | 0 |
| 1 | [2,3,1,2,3,1] | none | 0 |
| 2 | [1,2,3,1,2,3] | 0,1,2,3,4,5 | 6 |
| 3 | [3,1,2,3,1,2] | none | 0 |
| 4 | [2,3,1,2,3,1] | none | 0 |
| 5 | [1,2,3,1,2,3] | 0,1,2,3,4,5 | 6 |
Maximum match count:
6
Example 2
nums1 = [1,4,2,5,3,1]
nums2 = [2,3,1,2,4,6]
| Shift | Shifted nums1 | Matching Indices | Match Count |
|---|---|---|---|
| 0 | [1,4,2,5,3,1] | none | 0 |
| 1 | [1,1,4,2,5,3] | index 3 | 1 |
| 2 | [3,1,1,4,2,5] | index 2 | 1 |
| 3 | [5,3,1,1,4,2] | indices 1,2,4 | 3 |
| 4 | [2,5,3,1,1,4] | index 0 | 1 |
| 5 | [4,2,5,3,1,1] | none | 0 |
Maximum match count:
3
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n²) | We test all n shifts, each requiring n comparisons |
| Space | O(1) | Only a few integer variables are used |
The algorithm performs a nested loop structure. The outer loop iterates over every possible circular shift, and the inner loop checks all positions for matches. Since both loops run n times, the total complexity is O(n²).
No auxiliary arrays or hash maps are created, so the extra space usage remains constant.
Test Cases
sol = Solution()
# Provided example 1
assert sol.maximumMatchingIndices(
[3,1,2,3,1,2],
[1,2,3,1,2,3]
) == 6
# Provided example 2
assert sol.maximumMatchingIndices(
[1,4,2,5,3,1],
[2,3,1,2,4,6]
) == 3
# Arrays already identical
assert sol.maximumMatchingIndices(
[1,2,3],
[1,2,3]
) == 3
# No matching values at all
assert sol.maximumMatchingIndices(
[1,2,3],
[4,5,6]
) == 0
# Single element equal
assert sol.maximumMatchingIndices(
[7],
[7]
) == 1
# Single element not equal
assert sol.maximumMatchingIndices(
[7],
[8]
) == 0
# Duplicate values everywhere
assert sol.maximumMatchingIndices(
[1,1,1],
[1,1,1]
) == 3
# Best shift is not zero
assert sol.maximumMatchingIndices(
[1,2,3,4],
[3,4,1,2]
) == 4
# Multiple shifts give same answer
assert sol.maximumMatchingIndices(
[1,2,1,2],
[2,1,2,1]
) == 4
# Partial matching only
assert sol.maximumMatchingIndices(
[1,2,3,4],
[2,1,4,3]
) == 2
| Test | Why |
|---|---|
| Example 1 | Validates complete alignment after shifting |
| Example 2 | Validates partial matching |
| Already identical arrays | Ensures zero shifts are considered |
| No common values | Ensures answer can be zero |
| Single equal element | Smallest valid matching input |
| Single unequal element | Smallest non-matching input |
| All duplicates | Ensures duplicate handling works |
| Non-zero optimal shift | Verifies shifting logic |
| Multiple optimal shifts | Ensures all shifts are checked |
| Partial matching | Tests intermediate cases |
Edge Cases
One important edge case is when the arrays are already identical. A buggy implementation might assume at least one shift must occur, but the problem allows zero shifts. The algorithm handles this naturally because it checks shift 0 as part of the iteration.
Another critical edge case involves arrays with duplicate values. When duplicates exist, multiple different shifts can produce the same number of matches. Some incorrect solutions attempt to optimize using unique-value assumptions, which would fail here. This implementation compares positions directly for every shift, so duplicates are handled correctly without special logic.
A third important edge case is arrays of length one. Circular shifting a single-element array has no effect, so the answer depends entirely on whether the two elements are equal. The modular arithmetic still works correctly because:
(0 - shift + 1) % 1 = 0
Thus the implementation handles this case without requiring any special conditions.
Another subtle case is when no elements ever match. Some implementations initialize the answer incorrectly or assume at least one match exists. Since max_matches starts at 0, the algorithm correctly returns 0 when no alignment produces a match.