04
At Most / At Least K (counting) (Sliding Window Sub-pattern)
Count how many subarrays satisfy a rule instead of finding the best one. For an at most rule, every start from left to right is valid for the current right end. For an at least rule, it flips: once the window is good, every start before left (or every extension to the right) is good too.
Signals in the problem statement
- ›
"count the number of subarrays…"
- ›
"product less than k" / "score less than k"
- ›
"at most k distinct" + count
- ›
"contains at least one of each…" / "appears at least k times"
What makes it different
The answer is a count. At most → add right − left + 1 per step. At least → shrink while the window is still good, then add left (all starts before left work).
Questions
ordered from “learn the idea” to “recognise it without help”- 1Subarrays With At Most K Distinct IntegersWalkthroughMediumBasic Anchor·GeeksforGeeks
- 2Subarray Product Less Than KWalkthroughMediumBasic Reinforcement·LeetCode #713
- 3Number of Substrings Containing All Three CharactersWalkthroughMediumSlight Variation·LeetCode #1358
- 4Count Subarrays Where Max Element Appears at Least K TimesWalkthroughMediumMedium·LeetCode #2962
- 5Count Subarrays With Score Less Than KWalkthroughHardPattern Transfer·LeetCode #2302
Template
atmost-template.cpp
// Count subarrays satisfying an "at most" rule.
// For a fixed right end, every start in [left..right] is valid.
long long countAtMost(vector<int>& a, int k) {
if (k < 0) return 0; // "at most -1" is impossible (use k <= 0 for distinct counts)
long long count = 0;
int left = 0;
for (int right = 0; right < (int)a.size(); right++) {
add(a[right]);
while (!valid(k)) { remove(a[left]); left++; }
count += right - left + 1; // subarrays ending at right
}
return count;
}
// "Exactly K" is not monotonic, so use two "at most" counts:
long long countExactly(vector<int>& a, int k) {
return countAtMost(a, k) - countAtMost(a, k - 1);
}Remember this · Sliding Window
Trigger
Contiguous subarray / substring + longest, shortest, maximum, count, or "of size K".
Think
Can I keep a valid range and move it, instead of checking every subarray from scratch?
Key question
When is my current window valid?