此时,我们在数组 P 上二分查找到的下标即为 left 本身,同时我们也避免了原先 left}=0 时 left}-1=-1 不在数组合法的下标范围中的边界情况。
代码
[sol1-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: intlongestOnes(vector<int>& nums, int k){ int n = nums.size(); vector<int> P(n + 1); for (int i = 1; i <= n; ++i) { P[i] = P[i - 1] + (1 - nums[i - 1]); }
int ans = 0; for (int right = 0; right < n; ++right) { int left = lower_bound(P.begin(), P.end(), P[right + 1] - k) - P.begin(); ans = max(ans, right - left + 1); } return ans; } };
classSolution { publicintlongestOnes(int[] nums, int k) { intn= nums.length; int[] P = newint[n + 1]; for (inti=1; i <= n; ++i) { P[i] = P[i - 1] + (1 - nums[i - 1]); }
intans=0; for (intright=0; right < n; ++right) { intleft= binarySearch(P, P[right + 1] - k); ans = Math.max(ans, right - left + 1); } return ans; }
publicintbinarySearch(int[] P, int target) { intlow=0, high = P.length - 1; while (low < high) { intmid= (high - low) / 2 + low; if (P[mid] < target) { low = mid + 1; } else { high = mid; } } return low; } }
[sol1-Python3]
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution: deflongestOnes(self, nums: List[int], k: int) -> int: n = len(nums) P = [0] for num in nums: P.append(P[-1] + (1 - num)) ans = 0 for right inrange(n): left = bisect.bisect_left(P, P[right + 1] - k) ans = max(ans, right - left + 1) return ans
var longestOnes = function(nums, k) { const n = nums.length; const P = newArray(n + 1).fill(0); for (let i = 1; i <= n; ++i) { P[i] = P[i - 1] + (1 - nums[i - 1]); }
let ans = 0; for (let right = 0; right < n; ++right) { const left = binarySearch(P, P[right + 1] - k); ans = Math.max(ans, right - left + 1); } return ans; };
funclongestOnes(nums []int, k int) (ans int) { n := len(nums) P := make([]int, n+1) for i, v := range nums { P[i+1] = P[i] + 1 - v } for right, v := range P { left := sort.SearchInts(P, v-k) ans = max(ans, right-left) } return }
funcmax(a, b int)int { if a > b { return a } return b }
intbinarySearch(int* P, int len, int target) { int low = 0, high = len - 1; while (low < high) { int mid = (high - low) / 2 + low; if (P[mid] < target) { low = mid + 1; } else { high = mid; } } return low; }
intlongestOnes(int* nums, int numsSize, int k) { int P[numsSize + 1]; P[0] = 0; for (int i = 1; i <= numsSize; ++i) { P[i] = P[i - 1] + (1 - nums[i - 1]); }
int ans = 0; for (int right = 0; right < numsSize; ++right) { int left = binarySearch(P, numsSize + 1, P[right + 1] - k); ans = fmax(ans, right - left + 1); } return ans; }
复杂度分析
时间复杂度:O(n \log n),其中 n 是数组 A 的长度。每一次二分查找的时间复杂度为 O(\log n),我们需要枚举 right 进行 n 次二分查找,因此总时间复杂度为 O(n \log n)。
空间复杂度:O(n),即为前缀和数组 P 需要的空间。
方法二:滑动窗口
思路与算法
我们继续观察 (1) 式,由于前缀和数组 P 是单调递增的,那么 (1) 式的右侧 P[\textit{right}] - k 同样也是单调递增的。因此,我们可以发现:
随着 right 的增大,满足 (1) 式的最小的 left 值是单调递增的。
这样一来,我们就可以使用滑动窗口来实时地维护 left 和 right 了。在 right 向右移动的过程中,我们同步移动 left,直到 left 为首个(即最小的)满足 (1) 式的位置,此时我们就可以使用此区间对答案进行更新了。
细节
当我们使用滑动窗口代替二分查找解决本题时,就不需要显式地计算并保存出前缀和数组了。我们只需要知道 left 和 right 作为下标在前缀和数组中对应的值,因此我们只需要用两个变量 lsum 和 rsum 记录 left 和 right 分别对应的前缀和即可。
代码
[sol2-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: intlongestOnes(vector<int>& nums, int k){ int n = nums.size(); int left = 0, lsum = 0, rsum = 0; int ans = 0; for (int right = 0; right < n; ++right) { rsum += 1 - nums[right]; while (lsum < rsum - k) { lsum += 1 - nums[left]; ++left; } ans = max(ans, right - left + 1); } return ans; } };
[sol2-Java]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { publicintlongestOnes(int[] nums, int k) { intn= nums.length; intleft=0, lsum = 0, rsum = 0; intans=0; for (intright=0; right < n; ++right) { rsum += 1 - nums[right]; while (lsum < rsum - k) { lsum += 1 - nums[left]; ++left; } ans = Math.max(ans, right - left + 1); } return ans; } }
[sol2-Python3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: deflongestOnes(self, nums: List[int], k: int) -> int: n = len(nums) left = lsum = rsum = 0 ans = 0 for right inrange(n): rsum += 1 - nums[right] while lsum < rsum - k: lsum += 1 - nums[left] left += 1 ans = max(ans, right - left + 1) return ans
[sol2-JavaScript]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
var longestOnes = function(nums, k) { const n = nums.length; let left = 0, lsum = 0, rsum = 0; let ans = 0; for (let right = 0; right < n; ++right) { rsum += 1 - nums[right]; while (lsum < rsum - k) { lsum += 1 - nums[left]; ++left; } ans = Math.max(ans, right - left + 1); } return ans; };
[sol2-Golang]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
funclongestOnes(nums []int, k int) (ans int) { var left, lsum, rsum int for right, v := range nums { rsum += 1 - v for lsum < rsum-k { lsum += 1 - nums[left] left++ } ans = max(ans, right-left+1) } return }
funcmax(a, b int)int { if a > b { return a } return b }
[sol2-C]
1 2 3 4 5 6 7 8 9 10 11 12 13
intlongestOnes(int* nums, int numsSize, int k) { int left = 0, lsum = 0, rsum = 0; int ans = 0; for (int right = 0; right < numsSize; ++right) { rsum += 1 - nums[right]; while (lsum < rsum - k) { lsum += 1 - nums[left]; ++left; } ans = fmax(ans, right - left + 1); } return ans; }
复杂度分析
时间复杂度:O(n),其中 n 是数组 A 的长度。我们至多只需要遍历该数组两次(左右指针各一次)。