原題
In an array A containing only 0s and 1s, a K-bit flip consists of choosing a (contiguous) subarray of length K and simultaneously changing every 0 in the subarray to 1, and every 1 in the subarray to 0.
Return the minimum number of K-bit flips required so that there is no 0 in the array. If it is not possible, return -1.
Example 1:
Input: A = [0,1,0], K = 1
Output: 2
Explanation: Flip A[0], then flip A[2].
Example 2:
Input: A = [1,1,0], K = 2
Output: -1
Explanation: No matter how we flip subarrays of size 2, we can't make the array become [1,1,1].
Example 3:
Input: A = [0,0,0,1,0,1,1,0], K = 3
Output: 3
Explanation:
Flip A[0],A[1],A[2]: A becomes [1,1,1,1,0,1,1,0]
Flip A[4],A[5],A[6]: A becomes [1,1,1,1,1,0,0,0]
Flip A[5],A[6],A[7]: A becomes [1,1,1,1,1,1,1,1]
Note:
1 <= A.length <= 30000
1 <= K <= A.length
分析
在給出一個0, 1序列後,可以對其中連續K個數字進行翻轉(0翻轉爲1,1翻轉爲0)。如果想要全部翻轉爲1,那麼直觀的一個感受就是從左到右,如果當前位置的數字是0,那麼可以作爲需要翻轉的連續K個數字的起點,因此最簡單的思路:
- 從左到右,尋找值爲0的下標index
- 對A[index, index + K-1]的數字進行翻轉
- 重複1, 2,直至數組值全爲1,或者index + K > A.length爲止
拍腦門解法
Note:需要注意的一點是,在進行翻轉的時候所採取的方法決定是否超時,用^進行異或操作會減少用時,特別地,與1異或表示對當前位進行翻轉
Accept代碼
class Solution {
public:
bool flip(vector<int> & A, int K, int pos) {
if (pos + K > A.size())
return false;
for (int i = pos; i < pos + K; ++i) {
A[i] ^= 1;
}
return true;
}
int minKBitFlips(vector<int>& A, int K) {
int n = A.size();
int ans = 0, last_pos = 0;
for (int i = 0; i < n; ++i) {
if (A[i] == 0) {
ans++;
if (!flip(A, K, i))
return -1;
}
}
return ans;
}
};
在上面的解法中,之前用if else語句對A數組值進行翻轉,結果超時。
bool flip(vector<int> & A, int K, int pos) {
if (pos + K > A.size())
return false;
for (int i = pos; i < pos + K; ++i) {
//超時代碼
if(A[i] == 1)
A[i] = 0;
else
A[i] = 1;
}
return true;
}
這個方法速度很慢,用時5216 ms,在超時的邊緣徘徊,於是借鑑別人的解法。
他山之石,可以攻玉(別人的答案)
在提交之後可以看到別人提交的答案,看完後再看看自己的代碼便更覺得羞澀難擋,因此去掉了貼自己代碼這一步,轉而分析別人代碼。
作者@awice
思路:
之前的貪心算法雖然可以成功運行,但是耗時較長(主要耗時的部分在翻轉A[index, index+K-1]部分,當次翻轉範圍與上次翻轉範圍存在重疊,當重疊部分進行翻轉的兩次的時候,相當於沒有翻轉,造成了時間的浪費)。因此用一個flip位來記錄當前K個連續數字是否已經翻轉,用一個hint數組來記錄翻轉。
算法流程:
-
初始化flip =0, hint數組爲0
-
遍歷數組A,
-
flip ^= hint[i], 異或運算後flip位表示當前位置i是否被翻轉過,flip=0,未翻轉,flip=1,翻轉。hint數組用來記錄翻轉,(特別地,當A[i]需要翻轉時,hint[i + K]記錄翻轉截止範圍,見下面第4步驟)
-
判斷A[i] == flip,
- A[i]與flip均爲0時,表示A[i] = 0且當前位未被翻轉,需要進行翻轉
- A[i]與flip均爲1時,表示A[i] = 1且當前位已被翻轉(實際值爲0),需要再次翻轉
若A[i] == flip, 表明需要進行翻轉
- 翻轉次數加一,ans++
- 判斷i+K是否超出A數組長度,超出則失敗返回-1
- 對flip位進行翻轉
- 對hint[i+K]位進行翻轉
class Solution {
public:
int minKBitFlips(vector<int>& A, int K) {
int n = A.size();
vector<int> hint(n, 0);
int ans = 0, flip = 0;
for (int i = 0; i < n; ++i) {
flip ^= hint[i];
if (A[i] == flip) {
ans++;
if (i + K > n)
return -1;
flip ^= 1;
if (i + K < n)
hint[i + K] ^= 1;
}
}
return ans;
}
};
使用改進算法對重疊的部分進行索引記錄後,程序運行時間大大減少,爲112ms,縮短了近50倍。果然還是別人的飯吃起來香~