https://leetcode.com/problems/smallest-rotation-with-highest-score/
Given an array
A
, we may rotate it by a non-negative integer K
so that the array becomes A[K], A[K+1], A{K+2], ... A[A.length - 1], A[0], A[1], ..., A[K-1]
. Afterward, any entries that are less than or equal to their index are worth 1 point.
For example, if we have
[2, 4, 1, 3, 0]
, and we rotate by K = 2
, it becomes [1, 3, 0, 2, 4]
. This is worth 3 points because 1 > 0 [no points], 3 > 1 [no points], 0 <= 2 [one point], 2 <= 3 [one point], 4 <= 4 [one point].
Over all possible rotations, return the rotation index K that corresponds to the highest score we could receive. If there are multiple answers, return the smallest such index K.
Example 1: Input: [2, 3, 1, 4, 0] Output: 3 Explanation: Scores for each K are listed below: K = 0, A = [2,3,1,4,0], score 2 K = 1, A = [3,1,4,0,2], score 3 K = 2, A = [1,4,0,2,3], score 3 K = 3, A = [4,0,2,3,1], score 4 K = 4, A = [0,2,3,1,4], score 3
So we should choose K = 3, which has the highest score.
Example 2: Input: [1, 3, 0, 2, 4] Output: 0 Explanation: A will always have 3 points no matter how it shifts. So we will choose the smallest K, which is 0.
Note:
A
will have length at most20000
.A[i]
will be in the range[0, A.length]
.
Don't calculate the score for
(I see almost all other solutions did)
The key point is to find out how score changes when
K=0
, we don't need it at all.(I see almost all other solutions did)
The key point is to find out how score changes when
K++
Time complexity:
“A will have length at most 20000.”
I think it means you should find a O(N) solution.
“A will have length at most 20000.”
I think it means you should find a O(N) solution.
How dosen score change when K++ ?
- Get point
Each time when we rotate, we make index0
to indexN-1
, then we get one more point.
We know that for sure, so I don't need to record it. - Lose point
(i - A[i] + N) % N
is the value of K makingA[i]'s
index just equal toA[i]
.
For example, IfA[6] = 1
, thenK = (6 - A[6]) % 6 = 5
makingA[6]
toindex 1
of new array.
So whenK=5
, we get this point forA[6]
Then ifK
is bigger whenK = (i - A[i] + 1) % N
, we start to lose this point, making our score -= 1
All I have done is record the value ofK
for allA[i]
where we will lose points. A[i]=0
Rotation makes no change for it, becasue we alwars have0 <= index
.
However, it is covered in "get point" and "lose point".
Explanation of codes
- Search the index where score decrease and record this changement to a list
change
. - A simple for loop to calculate the score for every
K
value.score[K] = score[K-1] + change[K]
In my codes I accumulatedchanges
so I get the changed score for everyK
value compared toK=0
- Find the index of best score.
public int bestRotation(int[] A) {
int N = A.length;
int change[] = new int[N];
for (int i = 0; i < N; ++i) change[(i - A[i] + 1 + N) % N] -= 1;
int max_i = 0;
for (int i = 1; i < N; ++i) {
change[i] += change[i - 1] + 1;
max_i = change[i] > change[max_i] ? i : max_i;
}
return max_i;
}
https://leetcode.com/articles/smallest-rotation-with-highest-score/
Say
N = 10
and A[2] = 5
. Then there are 5 rotations that are bad for this number: rotation indexes 0, 1, 2, 8, 9
- these rotations will cause this number to not get 1 point later.
In general, for each number in the array, we can map out what rotation indexes will be bad for this number. It will always be a region of one interval, possibly two if the interval wraps around (eg.
8, 9, 0, 1, 2
wraps around, to become [8, 9]
and [0, 1, 2]
.)
At the end of plotting these intervals, we need to know which rotation index has the least intervals overlapping it - this one is the answer.
Algorithm
First, an element like
A[2] = 5
will not get score in (up to) 5 posiitons: when the 5 is at final index 0, 1, 2, 3, or 4. When we shift by 2, we'll get final index 0. If we shift 5-1 = 4
before this, this element will end up at final index 4. In general (modulo N), a shift of i - A[i] + 1
to i
will be the rotation indexes that will make A[i]
not score a point.
If we are trying to plot an interval like
[2, 3, 4]
, then instead of doing bad[2]--; bad[3]--; bad[4]--;
, what we will do instead is keep track of the cumulative total: bad[2]--; bad[5]++
. For "wrap-around" intervals like [8, 9, 0, 1, 2]
, we will keep track of this as two separate intervals: bad[8]--, bad[10]++, bad[0]--, bad[3]++
. (Actually, because of our implementation, we don't need to remember the bad[10]++
part.)
At the end, we want to find a rotation index with the least intervals overlapping. We'll maintain a cumulative total
cur
representing how many intervals are currently overlapping our current rotation index, then update it as we step through each rotation index.public int bestRotation(int[] A) { int N = A.length; int[] bad = new int[N]; for (int i = 0; i < N; ++i) { int left = (i - A[i] + 1 + N) % N; int right = (i + 1) % N; bad[left]--; bad[right]++; if (left > right) bad[0]--; } int best = -N; int ans = 0, cur = 0; for (int i = 0; i < N; ++i) { cur += bad[i]; if (cur > best) { best = cur; ans = i; } } return ans; }https://leetcode.com/problems/smallest-rotation-with-highest-score/discuss/118839/Simple-Java-O(n)-Solution
The trick of this problem is to convert it into finding most overlap interval problem (like meeting room II, finding most airplanes at a time)
public int bestRotation(int[] A) {
int n = A.length;
int[] a = new int[n]; // to record interval start/end
for (int i = 0; i < A.length; i++) {
a[(i + 1) % n]++; // interval start
a[(i + 1 - A[i] + n) % n]--; // interval end
}
int count = 0;
int maxCount = -1;
int res = 0;
for (int i = 0; i < n; i++) { // find most overlap interval
count += a[i];
if (count > maxCount) {
res = i;
maxCount = count;
}
}
return res;
}
https://leetcode.com/problems/smallest-rotation-with-highest-score/discuss/118671/Java-O(n)Time-O(n)-Space-Solution
Let's go through an example first. If the input is{2,4,1,3,0}, We write down all status when rotate k steps.
Number:::::: 2 4 1 3 0
k = 0 index: 0 1 2 3 4
k = 1 index: 4 0 1 2 3
k = 2 index: 3 4 0 1 2
k = 3 index: 2 3 4 0 1
k = 4 index: 1 2 3 4 0
Number:::::: 2 4 1 3 0
k = 0 index: 0 1 2 3 4
k = 1 index: 4 0 1 2 3
k = 2 index: 3 4 0 1 2
k = 3 index: 2 3 4 0 1
k = 4 index: 1 2 3 4 0
If we look in each column, then index for one particular number is somehow decrease with a valley(0). We want to know in which K steps the index is greate or equal than the corresponding number. For number 2, k should be{1,2,3}, for number 1, k should be{0,1,3,4}.
So our task is to collect all k rows in which the index >= number for each paritular number.
So our task is to collect all k rows in which the index >= number for each paritular number.
- If number > index in original array:
As index is decreasing, so we need to find in which row the index is equals to (length - 1), the largest index of array. Then go down until index == number. - If number <= index in original array:
k = 0 is always valid, and 0 through the row in which index == number is the last valid row. Also, don't forget the index == len - 1 this corner case;
There are some math tricks from maybe high school: largest index row = (index + 1) % len; Row in which index == number = (index + len - num) % len;
We just need to update count[min] and count[max] to avoid O(n) time to record our result.
We just need to update count[min] and count[max] to avoid O(n) time to record our result.
public int bestRotation(int[] A) {
int[] count = new int[A.length + 1];
int len = A.length;
for(int index = 0; index < A.length; index ++){
int num = A[index];
if(index < num){
int maxLenK = (index + 1) % len;
int ori = (index + len - num) % len;
count[maxLenK] ++;
count[ori + 1] --;
}else{ // index >= num
int ori = (index + len - num) % len;
count[0] ++;
count[ori + 1] --;
if(index != len - 1){
count[(index + 1) % len] ++;
count[len] --;
}
}
}
int max = count[0];
int res = 0;
for(int i = 1; i < len; i ++){
count[i] = count[i] + count[i - 1];
if(count[i] > max){
max = count[i];
res = i;
}
}
return res;
}
(区间标记)
- 对于每个数字,求出其能得分的轮调区间。比如样例 1 中下标为
1
位置的数字3
,其能得分的轮调区间为[2, 3]
。再比如下标为2
的数字1
,其能得分的区间有两段,分别为[0, 1]
和[3, 4]
。 - 具体地,每个数字最多有两段能得分的区间。假设数组长度为
n
,对于在下标为i
位置的数字A[i]
,如果A[i] >= n
,则得分区间不存在;在A[i] < n
的情况下,如果A[i] > i
,则得分区间为[i + 1, i + n - A[i]]
;如果A[i] < i
,则得分区间为[0, i - A[i]]
和[i + 1, n - 1]
。 - 得到了每个数的合法区间,我们通过区间标记的方法求出得分最大的点。如果
[l, r]
是一个得分区间,则我们标记valid[l]++
且valid[r + 1]--
,然后求出valid
数组的前缀和。数组中最大值的点就是得分最大的点。
https://www.acwing.com/solution/LeetCode/content/2552/
给定一个数组
A
,我们可以将它按一个非负整数 K
进行轮调,这样可以使数组变为 A[K], A[K+1], A[K+2], ... A[A.length - 1], A[0], A[1], ..., A[K-1]
的形式。此后,任何值小于或等于其索引的项都可以记作 1 分。
例如,如果数组为
[2, 4, 1, 3, 0]
,我们按 K = 2
进行轮调后,它将变成 [1, 3, 0, 2, 4]
。这将记作 3 分,因为 1 > 0 [没有得分], 3 > 1 [没有得分], 0 <= 2 [1 分], 2 <= 3 [1 分], 4 <= 4 [1 分]。
在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调索引 K。如果有多个答案,返回满足条件的最小的索引 K。
样例
输入:[2, 3, 1, 4, 0]
输出:3
解释:
下面列出了每个 K 的得分:
K = 0, A = [2,3,1,4,0], score 2
K = 1, A = [3,1,4,0,2], score 3
K = 2, A = [1,4,0,2,3], score 3
K = 3, A = [4,0,2,3,1], score 4
K = 4, A = [0,2,3,1,4], score 3
所以我们应当选择 K = 3,得分最高。
输入:[1, 3, 0, 2, 4]
输出:0
解释:
无论怎么变化总是有 3 分。
所以我们将选择最小的 ,即 0。