연속 구간을 창문 밀듯 갱신 — 빠진 값 빼고 새 값만 더하기. 고정 크기에서 시작해 가변 크기(두 포인터 결합)로 확장합니다.
지문 신호“연속한 K개”“연속 부분 배열”“최장/최단 구간”
0/8 해결
1
눈으로 보기
재생을 누르고 단계별로 동작을 따라가세요
2
5
1
4
3
2
합 = —최댓값 = —
1/7연속한 3개의 합의 최댓값을 찾습니다. 매번 다시 더하면 O(N·K) — 창문을 밀며 "빠진 값 빼고 새 값만 더하면" O(N)입니다.
■ 앰버 테두리 = 현재 창문(k=3)빠진 값 −, 새 값 + 만으로 갱신
2
개념 이해
핵심 패턴 코드는 손으로 따라 쳐 보는 것을 권장
1 / 8
비유로 이해하기
신문에 숫자가 한 줄로 인쇄되어 있고, 그 위에 구멍이 세 칸 뚫린 종이 액자를 올려놓았다고 생각해 보세요. 액자를 오른쪽으로 한 칸 밀면 왼쪽 끝의 숫자 하나가 가려지고, 오른쪽 끝에서 숫자 하나가 새로 보입니다. 나머지 숫자는 그대로입니다. 그래서 액자 안 숫자의 합을 다시 구할 때 전부 더할 필요 없이, 사라진 값을 빼고 새로 들어온 값을 더하면 됩니다.
슬라이딩 윈도우는 연속한 구간을 훑을 때, 구간을 매번 새로 계산하지 않고 한 칸 밀 때의 변화량만 갱신하는 기법입니다.
액자 위치 1: [3 1 4] 1 5 → 합 8
한 칸 밀기: 3이 나가고 1이 들어옴 → 8 - 3 + 1 = 6
다시 밀기: 1이 나가고 5가 들어옴 → 6 - 1 + 5 = 10
[3, 1, 4, 1, 5]에서 연속한 3개의 합이 가장 큰 구간을 손으로 찾아봅니다.
단계
액자가 덮은 구간
나간 값 / 들어온 값
합
지금까지 최대
시작
3, 1, 4
(첫 창은 그냥 다 더함)
8
8
1
1, 4, 1
3 나감 / 1 들어옴
8 - 3 + 1 = 6
8
2
4, 1, 5
1 나감 / 5 들어옴
6 - 1 + 5 = 10
10
정직하게 계산하면 구간마다 3번씩 더해 총 9번이지만, 밀면서 계산하면 첫 창 3번 + 이후 한 칸당 2번뿐입니다. 원소가 10만 개이고 구간 길이가 1000이면 이 차이가 그대로 시간 초과와 통과를 가릅니다.
고정 크기 창의 골격은 "첫 창을 만들고 → 한 칸씩 밀고 → 기록한다"입니다.
첫 창만 정직하게 채웁니다 — 인덱스 0부터 k-1까지 전부 더합니다.
let sum = 0;for (let i = 0; i < k; i++) sum += arr[i];let best = sum;
한 칸 밀 때는 빼고 더하기만 합니다 — 나가는 값은 arr[i - k], 들어오는 값은 arr[i]입니다.
sum += arr[i] - arr[i - k]; // 미는 비용은 O(1)
밀 때마다 답을 갱신합니다.
best = Math.max(best, sum);
전체 코드 — "연속 K개 합의 최댓값":
function maxWindowSum(arr: number[], k: number): number { // 1단계: 첫 창만 정직하게 let sum = 0; for (let i = 0; i < k; i++) sum += arr[i]; let best = sum; // 2~3단계: 한 칸씩 밀며 갱신하고 기록 for (let i = k; i < arr.length; i++) { sum += arr[i] - arr[i - k]; best = Math.max(best, sum); } return best;}
지문에 "연속한 K개", "연속 부분 배열", "연속한 부분 문자열"이 보이면 이 유형입니다. 연속이 아니라 아무 원소나 골라도 되는 문제는 슬라이딩 윈도우가 아닙니다.
"조건을 만족하는 가장 긴 / 가장 짧은 연속 구간의 길이" — 창 크기가 변하는 가변 윈도우 신호입니다.
대표 문제 상황: 연속 K일 매출의 최대 합, 서로 다른 문자가 K종 이하인 최장 부분 문자열, 합이 목표 이하인 최장 구간.
이중 루프로 모든 구간을 다시 더하다 시간 초과가 났다면 거의 이 기법으로 바꿔야 하는 경우입니다.
가변 크기 윈도우 (두 포인터 결합) — 조건을 만족하는 최장/최단 구간은 창 크기가 변합니다. 오른쪽을 늘리다 조건이 깨지면 왼쪽을 줄입니다.
// 합이 K 이하인 최장 연속 구간function longestUnderK(arr: number[], K: number): number { let left = 0, sum = 0, best = 0; for (let right = 0; right < arr.length; right++) { sum += arr[right]; // ① 오른쪽 확장 while (sum > K) sum -= arr[left++]; // ② 조건 깨지면 왼쪽 축소 best = Math.max(best, right - left + 1); // ③ 유효한 상태에서 기록 } return best;}
확장 → 축소 → 기록의 3박자를 몸에 익히세요. left와 right 모두 최대 N번 움직이므로 O(N)입니다.
창 안의 구성이 조건인 경우 — "서로 다른 문자 K종 이하 최장 부분 문자열"처럼 창 안에 무엇이 들어 있는지가 조건이면, 창과 함께 Map(문자 → 개수)을 유지합니다.