
난이도: Medium
주제: Array, Two Pointers
정수 배열 nums와 두 정수 left, right가 주어진다.
연속된 비어 있지 않은 부분배열 중, 그 부분배열의 최댓값이 [left, right] 범위에 들어가는 것의 개수를 구한다.
Input: nums = [2,1,4,3], left = 2, right = 3
Output: 3
nums.length ≤ 10^50 ≤ nums[i] ≤ 10^90 ≤ left ≤ right ≤ 10^9처음 읽으면 무슨 말인지 잘 안 들어온다. 예제를 전부 펼쳐 보면 명확해진다.
[2,1,4,3], 범위 [2, 3]일 때 가능한 부분배열 10개:
| 부분배열 | 최댓값 | 판정 |
|---|---|---|
[2] | 2 | ✅ |
[2,1] | 2 | ✅ |
[2,1,4] | 4 | ❌ 초과 |
[2,1,4,3] | 4 | ❌ 초과 |
[1] | 1 | ❌ 미달 |
[1,4] | 4 | ❌ 초과 |
[1,4,3] | 4 | ❌ 초과 |
[4] | 4 | ❌ 초과 |
[4,3] | 4 | ❌ 초과 |
[3] | 3 | ✅ |
즉 "최댓값이 범위 안에 드는 부분배열만 세라" 는 문제다.
모든 (i, j) 쌍에 대해 최댓값을 구하면 O(n²). n = 10^5에서 터진다.
핵심은 "i번째 원소에서 끝나는 유효 부분배열이 몇 개인가" 를 한 번의 순회로 구하는 것이다. 각 원소는 세 종류 중 하나다.
nums[i] > right (벽)left ≤ nums[i] ≤ right (유효)i - lastWall.nums[i] < left (미달)function numSubarrayBoundedMax(nums: number[], left: number, right: number): number {
let bounded = 0; // 정답 누적
let valid = 0; // i에서 끝나는 유효 부분배열 개수
let lastOverIdx = -1; // 마지막으로 right를 초과한 인덱스
for (let i = 0; i < nums.length; i++) {
if (nums[i] > right) {
valid = 0;
lastOverIdx = i;
} else if (nums[i] >= left) {
valid = i - lastOverIdx;
}
// nums[i] < left 이면 valid를 그대로 유지
bounded += valid;
}
return bounded;
}
[2,9,2,5,6], 범위 [2, 8]:
| i | nums[i] | 종류 | lastOverIdx | valid | bounded |
|---|---|---|---|---|---|
| 0 | 2 | 유효 | -1 | 1 | 1 |
| 1 | 9 | 벽 | 1 | 0 | 1 |
| 2 | 2 | 유효 | 1 | 1 | 2 |
| 3 | 5 | 유효 | 1 | 2 | 4 |
| 4 | 6 | 유효 | 1 | 3 | 7 |
정답 7.
valid를 유지만 하면 된다.