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]
.
這道題給了我們一個長度為N的數組,說是數組中的數字的范圍都在[0, N]之間,然后定義了一個旋轉操作,比如在位置K進行旋轉,數組在K位置斷開,新數組以A[k]為開頭數字,斷開的前半段數組直接拼到末尾即可。然后又定義了一種積分規則,說是如果某個坐標位置大於等於其數字的話,得1分,讓我們求出某個旋轉位置K,使得其積分最大,如果積分相同的話,取位置小的K。通過分析題目中的例子,發現題目並不難理解。博主首先嘗試了暴力搜索的方法,就是遍歷每個K值,生成旋轉后的數組,然后再統計得分,不幸掛掉了。那么我們必須要想出更好的解法才行。首先我們想,如果數組中的每個數字都跟其坐標值相同的話,比如[0, 1, 2, 3, 4],那么肯定得分最高,即K=0。但實際上並不會是有序的,而且並不是每個數字都會出現,題目中只給了數字的范圍,有可能會有重復數字哦。說實話這道題博主研究大神lee215的帖子,都研究了好久,最后終於有些明白了,就大概講講吧,如果有不對的地方歡迎大家指正。
這道題博主感覺還是很有難度的,而且答案的思路也十分巧妙,並沒有采用brute force那種直接求每一個K值的得分,而是反其道而行之,對於每個數字,探究其跟K值之間的聯系。首先我們要討論一下邊界情況,那么就是當A[i] = 0 或 N 的情況,首先如果A[i] = 0的話,那么0這個數字在任何位置都會小於等於坐標值,所以在任何位置都會得分的,那么其實可以忽略之,因為其不會對最大值產生任何影響,同理,如果A[i] = N的時候,由於長度為N的數組的坐標值范圍是[0, N-1],所以數字N在任何位置都不得分,同樣也不會對最大值產生任何影響,可以忽略之。那么我們關心的數字的范圍其實是[1, N-1]。在這個范圍內的數字在旋轉數組的過程中,從位置0變到N-1位置的時候,一定會得分,因為此范圍的數字最大就是N-1。這個一定得的分我們在最后統一加上,基於上面的發現,我們再來分析下題目中的例子 [2, 3, 1, 4, 0],其中紅色數字表示不得分的位置:
A: 2 3 1 4 0 (K = 0)
A: 3 1 4 0 2 (K = 1)
A: 1 4 0 2 3 (K = 2)
A: 4 0 2 3 1 (K = 3)
A: 0 2 3 1 4 (K = 4)
idx: 0 1 2 3 4
對於某個數字A[i],我們想知道其什么時候能旋轉到坐標位置為A[i]的地方,這樣就可以得分了。比如上面博主標記了紅色的數字3,最開始時的位置為1,此時是不得分的,我們想知道其什么時候能到位置3,答案是當K=3的時候,其剛好旋轉到位置3,K再增加的時候,其又開始不得分了。所以這個最后能得分的臨界位置是通過 (i - A[i] + N) % N 得到,那么此時如果K再增加1的話,A[i]就開始不得分了(如果我們suppose每個位置都可以得分,那么不得分的地方就可以當作是失分了),所以我們可以在這個剛好開始不得分的地方標記一下,通過-1進行標記,這個位置就是 (i - A[i] + 1 + N) % N。我們用一個長度為N的change數組,對於每個數字,我們都找到其剛好不得分的地方,進行-1操作,那么此時change[i]就表示數組中的數字在i位置會不得分的個數,如果我們仔細觀察上面紅色的數字,可以發現,由於是左移,坐標在不斷減小,所以原先失分的地方,在K+1的時候還是失分,除非你從開頭位置跑到末尾去了,那會得分,所以我們要累加change數組,並且K每增加1的時候,要加上額外的1,最后change數組中最大數字的位置就是要求的K值了,參見代碼如下:
解法一:
class Solution { public: int bestRotation(vector<int>& A) { int n = A.size(); vector<int> change(n, 0); for (int i = 0; i < n; ++i) change[(i - A[i] + 1 + n) % n] -= 1; for (int i = 1; i < n; ++i) change[i] += change[i - 1] + 1; return max_element(change.begin(), change.end()) - change.begin(); } };
我們也可以不用STL自帶的max_element函數,而是在遍歷的過程中同時找最大值即可,參見代碼如下:
解法二:
class Solution { public: int bestRotation(vector<int>& A) { int n = A.size(), res = 0; vector<int> change(n, 0); for (int i = 0; i < n; ++i) change[(i - A[i] + 1 + n) % n] -= 1; for (int i = 1; i < n; ++i) { change[i] += change[i - 1] + 1; res = (change[i] > change[res]) ? i : res; } return res; } };
參考資料: