上週末終於有空參加LeetCode的比賽了,而且這還是我第一次在比賽中完成所有的題目。雖然加上罰時超了比賽時間,但和以前相比也算是很大的進步。這次的題目比較簡單,前兩道就是暴力解法,第三道是動態規劃的簡單應用,最後一道比較有意思,我一開始超時了,後來發現可以用滑動窗口的思想優化。滑動窗口即Sliding Window,其實我不確定滑動窗口是不是一個算法,因爲我在書上並沒有看到過這個算法的介紹,但滑動窗口的思想是非常棒的,在很多題目中都能用到,接下來就以這次的最後一題爲例介紹滑動窗口。
滑動窗口
滑動窗口即在數組上維護一個區間,不斷調整首尾直到遍歷數組。適用滑動窗口的題目往往要求找到數組中的一個區間,如果遍歷數組中所有的區間,時間複雜度爲O(n2),而滑動窗口的時間複雜度爲O(n)。
Smallest Rotation with Highest Score
問題
給出一個數組,可以將這個數組的每一位元素往左移(越界的元素補到最右邊)得到一個新的數組,長度爲n
的數組一共可以得到n
個不同的數組。現在給每個數組打分,一個數組每一位元素的值若不大於下標則得1
分,求最高分的數組。
思路
一開始我想得很簡單,求出每個數組的分數再取最高分的數組,結果超時了。我在求數組分數的時候是遍歷數組的每一位元素,對每一位元素對應可以得分的數組加1
分,即每一位元素的操作時間複雜度爲O(n)
。事實上每一位元素對應可以得分的數組是連續的,可以看作區間僅記錄首尾,即第一個數組得分比前一個數組多1
,最後一個數組得分比後一個數組多1
。這樣我們就可以得到每一個數組的分數與前一個數組分數的關係,遍歷一次就可以得到最高分的數組了。
代碼
class Solution {
public:
int bestRotation(vector<int>& A) {
vector<int> v(A.size()+1, 0); //調整長度防止越界
for (int i = 0; i < A.size(); ++i) {
if (A[i] <= i) { //元素不大於下標
++v[0]; //從移動0位開始得分
--v[i + 1 - A[i]]; //左移下標不斷減小,直到元素大於下標,結束得分
++v[i + 1]; //元素左移越界,補到最右端,下標達到最大,開始得分
} else { //元素大於下標
++v[i + 1]; //左移下標不斷減小直到越界,元素補到最右端,下標達到最大,開始得分
--v[i + 1 + A.size() - A[i]]; //左移下標不斷減小,直到元素大於下標,結束得分
}
}
int K = 0;
int ans = v[0]; //記錄數組得分
int max = ans;
for (int i = 1; i < A.size(); ++i) {
ans += v[i]; //由前一個數組分數得到當前數組分數
if (ans > max) {
K = i;
max = ans;
}
}
return K;
}
};
感想
這次比賽完我覺得LeetCode的題目還是比較簡單的,雖然有時候需要一些巧妙的思路,但總體上不會太複雜,比較適合用來熟悉算法和學習思路。以後我打算找些複雜點的題目來做,比如CodeForce上的題,用來提高編程競賽的能力。這個學期我想把主要精力放在準備ACM校賽上,看看自己編程的極限在哪裏,希望對以後的學習和工作有所幫助。