[2026] LeetCode 패턴: 두 포인터와 슬라이딩 윈도우 | 템플릿과 C++/Python

[2026] LeetCode 패턴: 두 포인터와 슬라이딩 윈도우 | 템플릿과 C++/Python

이 글의 핵심

LeetCode 두 포인터·슬라이딩 윈도우 패턴의 차이, 고정·가변 윈도우 템플릿과 대표 문제 풀이를 C++와 Python으로 정리합니다.

들어가며

LeetCode 두 포인터 슬라이딩 윈도우는 배열·문자열 문제에서 가장 자주 등장하는 패턴입니다. 두 포인터는 “구간의 양 끝” 또는 “같은 방향으로 전진”으로 O(n) 탐색을 만들고, 슬라이딩 윈도우는 그중 연속 구간의 합·빈도·조건을 유지하며 최적해를 찾는 기법입니다. 이 글에서는 패턴별 템플릿을 먼저 외운 뒤, LeetCode 3 (Longest Substring Without Repeating Characters), 76 (Minimum Window Substring), 209 (Minimum Size Subarray Sum), 11 (Container With Most Water) 등의 대표 문제에 적용하는 흐름으로 설명합니다. 같은 로직을 C++와 Python에 옮겨 적어 면접·시험에서 바로 꺼내 쓸 수 있게 했습니다.

이 글을 읽으면

  • 두 포인터(대칭·동시 전진)와 슬라이딩 윈도우의 경계를 구분합니다
  • 고정 길이 vs 가변 길이 윈도우 템플릿을 바로 꺼내 씁니다
  • C++와 Python으로 동일 로직을 옮겨 적는 연습을 합니다

코딩 테스트 준비하며 깨달은 것

알고리즘 문제를 풀다 보면 “이게 실무에 무슨 도움이 될까?” 하는 의문이 들 때가 있습니다. 저도 그랬습니다. 하지만 실제 프로젝트에서 성능 문제에 부딪히면, 알고리즘 지식이 얼마나 중요한지 깨닫게 됩니다. 예를 들어, 사용자 검색 기능이 느려서 고민하다가 해시 테이블을 적용하니 응답 시간이 10초에서 0.1초로 줄어든 경험이 있습니다. 코딩 테스트는 단순히 취업을 위한 관문이 아니라, 문제 해결 능력을 키우는 훈련장입니다. 처음엔 브루트 포스로 풀다가, 시간 복잡도를 개선하는 과정에서 “아, 이렇게 생각하면 되는구나” 하는 깨달음을 얻을 때의 쾌감은 말로 표현하기 어렵습니다. 이 글에서는 단순히 정답 코드만 제시하는 게 아니라, 문제를 어떻게 접근하고 최적화하는지 사고 과정을 함께 공유하겠습니다.

목차

  1. 개념 설명
  2. 실전 구현
  3. 고급 활용
  4. 성능과 비교
  5. 실무 사례
  6. 트러블슈팅
  7. 마무리

개념 설명

두 포인터 (Two Pointers)

정의: 배열이나 문자열에서 두 개의 인덱스를 사용해 구간을 표현하고, 특정 조건을 만족하는 해를 찾는 기법입니다.

유형 1: 대칭형 (양 끝에서 안쪽으로)

아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 정렬된 배열에서 합이 target인 쌍 찾기
def two_sum_sorted(arr, target):
    left, right = 0, len(arr) - 1
    while left < right:
        current_sum = arr[left] + arr[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1
    return [-1, -1]

유형 2: 동시 전진 (같은 방향)

아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 중복 제거 (in-place)
def remove_duplicates(arr):
    if not arr:
        return 0
    slow = 0
    for fast in range(1, len(arr)):
        if arr[fast] != arr[slow]:
            slow += 1
            arr[slow] = arr[fast]
    return slow + 1

슬라이딩 윈도우 (Sliding Window)

정의: 두 포인터의 특수한 형태로, 연속 구간(윈도우)을 유지하며 조건을 만족하는 최적해를 찾습니다.

유형 1: 고정 길이 윈도우

아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 길이 k 구간의 최대 합
def max_sum_k(arr, k):
    window_sum = sum(arr[:k])
    max_sum = window_sum
    for i in range(k, len(arr)):
        window_sum += arr[i] - arr[i - k]
        max_sum = max(max_sum, window_sum)
    return max_sum

유형 2: 가변 길이 윈도우

아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 합이 target 이상인 최소 길이 구간
def min_subarray_len(target, arr):
    left = 0
    window_sum = 0
    min_len = float('inf')
    
    for right in range(len(arr)):
        window_sum += arr[right]
        while window_sum >= target:
            min_len = min(min_len, right - left + 1)
            window_sum -= arr[left]
            left += 1
    
    return min_len if min_len != float('inf') else 0

실전 구현

패턴 1: 가변 길이 윈도우 - 중복 없는 가장 긴 부분 문자열

문제: LeetCode 3 - Longest Substring Without Repeating Characters 입력: s = "abcabcbb"
출력: 3 (답: “abc”) 아이디어:

  1. right 포인터로 문자를 추가하며 빈도 카운트
  2. 중복 발생 시 left를 이동하며 중복 제거
  3. 매 단계마다 최대 길이 갱신

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <string>
#include <unordered_map>
#include <algorithm>
class Solution {
public:
    int lengthOfLongestSubstring(const std::string& s) {
        std::unordered_map<char, int> cnt;
        int left = 0, ans = 0;
        
        for (int right = 0; right < (int)s.size(); ++right) {
            cnt[s[right]]++;
            
            while (cnt[s[right]] > 1) {
                cnt[s[left]]--;
                if (cnt[s[left]] == 0) {
                    cnt.erase(s[left]);
                }
                left++;
            }
            
            ans = std::max(ans, right - left + 1);
        }
        
        return ans;
    }
};

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        cnt = {}
        left = 0
        ans = 0
        
        for right, ch in enumerate(s):
            cnt[ch] = cnt.get(ch, 0) + 1
            
            while cnt[ch] > 1:
                cnt[s[left]] -= 1
                if cnt[s[left]] == 0:
                    del cnt[s[left]]
                left += 1
            
            ans = max(ans, right - left + 1)
        
        return ans

시간 복잡도: O(n) — leftright 모두 최대 n번 이동
공간 복잡도: O(min(n, m)) — m은 문자 집합 크기

패턴 2: 최소 윈도우 - Minimum Window Substring

문제: LeetCode 76 - Minimum Window Substring 입력: s = "ADOBECODEBANC", t = "ABC"
출력: "BANC" 아이디어:

  1. t의 문자 빈도를 need에 저장
  2. right로 확장하며 윈도우 빈도 갱신
  3. 모든 문자가 만족되면 left를 당기며 최소 길이 갱신

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

from collections import Counter
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if not s or not t:
            return ""
        
        need = Counter(t)
        required = len(need)
        formed = 0
        window = {}
        
        left = 0
        best_len = float("inf")
        best = (0, 0)
        
        for right, c in enumerate(s):
            window[c] = window.get(c, 0) + 1
            
            if c in need and window[c] == need[c]:
                formed += 1
            
            while formed == required and left <= right:
                if right - left + 1 < best_len:
                    best_len = right - left + 1
                    best = (left, right)
                
                lch = s[left]
                window[lch] -= 1
                if lch in need and window[lch] < need[lch]:
                    formed -= 1
                left += 1
        
        if best_len == float("inf"):
            return ""
        l, r = best
        return s[l : r + 1]

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <string>
#include <unordered_map>
#include <climits>
class Solution {
public:
    std::string minWindow(const std::string& s, const std::string& t) {
        if (s.empty() || t.empty()) return "";
        
        std::unordered_map<char, int> need, window;
        for (char c : t) need[c]++;
        
        int required = need.size();
        int formed = 0;
        int left = 0;
        int best_len = INT_MAX;
        int best_left = 0;
        
        for (int right = 0; right < (int)s.size(); ++right) {
            char c = s[right];
            window[c]++;
            
            if (need.count(c) && window[c] == need[c]) {
                formed++;
            }
            
            while (formed == required && left <= right) {
                if (right - left + 1 < best_len) {
                    best_len = right - left + 1;
                    best_left = left;
                }
                
                char lch = s[left];
                window[lch]--;
                if (need.count(lch) && window[lch] < need[lch]) {
                    formed--;
                }
                left++;
            }
        }
        
        return best_len == INT_MAX ? "" : s.substr(best_left, best_len);
    }
};

시간 복잡도: O(n + m) — n은 s 길이, m은 t 길이
공간 복잡도: O(m)

패턴 3: 합 조건 - Minimum Size Subarray Sum

문제: LeetCode 209 - Minimum Size Subarray Sum 입력: target = 7, nums = [2,3,1,2,4,3]
출력: 2 (답: [4,3]) 아이디어:

  1. right로 확장하며 합 증가
  2. 합이 target 이상이면 left를 당기며 최소 길이 갱신

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def minSubArrayLen(self, target: int, nums: list[int]) -> int:
        left = 0
        window_sum = 0
        min_len = float('inf')
        
        for right in range(len(nums)):
            window_sum += nums[right]
            
            while window_sum >= target:
                min_len = min(min_len, right - left + 1)
                window_sum -= nums[left]
                left += 1
        
        return min_len if min_len != float('inf') else 0

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <vector>
#include <algorithm>
#include <climits>
class Solution {
public:
    int minSubArrayLen(int target, const std::vector<int>& nums) {
        int left = 0;
        int window_sum = 0;
        int min_len = INT_MAX;
        
        for (int right = 0; right < (int)nums.size(); ++right) {
            window_sum += nums[right];
            
            while (window_sum >= target) {
                min_len = std::min(min_len, right - left + 1);
                window_sum -= nums[left];
                left++;
            }
        }
        
        return min_len == INT_MAX ? 0 : min_len;
    }
};

시간 복잡도: O(n)
공간 복잡도: O(1)

패턴 4: 대칭형 두 포인터 - Container With Most Water

문제: LeetCode 11 - Container With Most Water 입력: height = [1,8,6,2,5,4,8,3,7]
출력: 49 (인덱스 1과 8 사이) 아이디어:

  1. 양 끝에서 시작
  2. 현재 면적 계산: min(height[left], height[right]) * (right - left)
  3. 더 낮은 쪽 포인터를 안쪽으로 이동

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def maxArea(self, height: list[int]) -> int:
        left, right = 0, len(height) - 1
        max_area = 0
        
        while left < right:
            width = right - left
            current_area = min(height[left], height[right]) * width
            max_area = max(max_area, current_area)
            
            if height[left] < height[right]:
                left += 1
            else:
                right -= 1
        
        return max_area

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <vector>
#include <algorithm>
class Solution {
public:
    int maxArea(const std::vector<int>& height) {
        int left = 0, right = height.size() - 1;
        int max_area = 0;
        
        while (left < right) {
            int width = right - left;
            int current_area = std::min(height[left], height[right]) * width;
            max_area = std::max(max_area, current_area);
            
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        
        return max_area;
    }
};

시간 복잡도: O(n)
공간 복잡도: O(1)

패턴 5: 고정 길이 윈도우 - Maximum Average Subarray

문제: LeetCode 643 - Maximum Average Subarray I 입력: nums = [1,12,-5,-6,50,3], k = 4
출력: 12.75 (답: [12,-5,-6,50]) 아이디어:

  1. 첫 k개 합 계산
  2. 윈도우를 한 칸씩 이동하며 합 갱신

Python 구현

아래 코드는 python를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def findMaxAverage(self, nums: list[int], k: int) -> float:
        window_sum = sum(nums[:k])
        max_sum = window_sum
        
        for i in range(k, len(nums)):
            window_sum += nums[i] - nums[i - k]
            max_sum = max(max_sum, window_sum)
        
        return max_sum / k

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <vector>
#include <algorithm>
#include <numeric>
class Solution {
public:
    double findMaxAverage(const std::vector<int>& nums, int k) {
        int window_sum = std::accumulate(nums.begin(), nums.begin() + k, 0);
        int max_sum = window_sum;
        
        for (int i = k; i < (int)nums.size(); ++i) {
            window_sum += nums[i] - nums[i - k];
            max_sum = std::max(max_sum, window_sum);
        }
        
        return static_cast<double>(max_sum) / k;
    }
};

시간 복잡도: O(n)
공간 복잡도: O(1)

패턴 6: 문자 빈도 - Permutation in String

문제: LeetCode 567 - Permutation in String 입력: s1 = "ab", s2 = "eidbaooo"
출력: true (s2에 “ba” 포함) 아이디어:

  1. s1의 문자 빈도 저장
  2. s2에서 길이 len(s1) 윈도우를 슬라이드하며 빈도 비교

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

from collections import Counter
class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s1) > len(s2):
            return False
        
        need = Counter(s1)
        window = Counter(s2[:len(s1)])
        
        if window == need:
            return True
        
        for i in range(len(s1), len(s2)):
            window[s2[i]] += 1
            
            left_char = s2[i - len(s1)]
            window[left_char] -= 1
            if window[left_char] == 0:
                del window[left_char]
            
            if window == need:
                return True
        
        return False

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <string>
#include <unordered_map>
class Solution {
public:
    bool checkInclusion(const std::string& s1, const std::string& s2) {
        if (s1.size() > s2.size()) return false;
        
        std::unordered_map<char, int> need, window;
        for (char c : s1) need[c]++;
        
        for (int i = 0; i < (int)s1.size(); ++i) {
            window[s2[i]]++;
        }
        
        if (window == need) return true;
        
        for (int i = (int)s1.size(); i < (int)s2.size(); ++i) {
            window[s2[i]]++;
            
            char left_char = s2[i - s1.size()];
            window[left_char]--;
            if (window[left_char] == 0) {
                window.erase(left_char);
            }
            
            if (window == need) return true;
        }
        
        return false;
    }
};

시간 복잡도: O(n) — 윈도우 비교는 O(26) = O(1)
공간 복잡도: O(1) — 알파벳 26개

패턴 7: 최대 K개 고유 문자 - Longest Substring with At Most K Distinct

문제: s에서 최대 k개 고유 문자를 포함하는 가장 긴 부분 문자열 입력: s = "eceba", k = 2
출력: 3 (답: “ece”)

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int:
        if k == 0:
            return 0
        
        window = {}
        left = 0
        max_len = 0
        
        for right, ch in enumerate(s):
            window[ch] = window.get(ch, 0) + 1
            
            while len(window) > k:
                left_char = s[left]
                window[left_char] -= 1
                if window[left_char] == 0:
                    del window[left_char]
                left += 1
            
            max_len = max(max_len, right - left + 1)
        
        return max_len

C++ 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <string>
#include <unordered_map>
#include <algorithm>
class Solution {
public:
    int lengthOfLongestSubstringKDistinct(const std::string& s, int k) {
        if (k == 0) return 0;
        
        std::unordered_map<char, int> window;
        int left = 0;
        int max_len = 0;
        
        for (int right = 0; right < (int)s.size(); ++right) {
            window[s[right]]++;
            
            while ((int)window.size() > k) {
                char left_char = s[left];
                window[left_char]--;
                if (window[left_char] == 0) {
                    window.erase(left_char);
                }
                left++;
            }
            
            max_len = std::max(max_len, right - left + 1);
        }
        
        return max_len;
    }
};

시간 복잡도: O(n)
공간 복잡도: O(k)

고급 활용

1) 배열 최적화 (소문자 알파벳 한정)

시나리오: 문자 빈도를 추적할 때 해시맵 대신 크기 26 배열 사용

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        cnt = [0] * 128  # ASCII
        left = 0
        ans = 0
        
        for right, ch in enumerate(s):
            cnt[ord(ch)] += 1
            
            while cnt[ord(ch)] > 1:
                cnt[ord(s[left])] -= 1
                left += 1
            
            ans = max(ans, right - left + 1)
        
        return ans

장점:

  • 해시맵보다 빠른 접근 (O(1) 보장)
  • 메모리 효율적

2) Deque를 활용한 최대/최소 추적

시나리오: 윈도우 내 최대값을 O(1)에 찾기 문제: LeetCode 239 - Sliding Window Maximum

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

from collections import deque
class Solution:
    def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
        dq = deque()
        result = []
        
        for i, num in enumerate(nums):
            while dq and nums[dq[-1]] < num:
                dq.pop()
            dq.append(i)
            
            if dq[0] <= i - k:
                dq.popleft()
            
            if i >= k - 1:
                result.append(nums[dq[0]])
        
        return result

시간 복잡도: O(n) — 각 원소가 deque에 최대 1번 삽입/삭제
공간 복잡도: O(k)

3) 다중 조건 윈도우

시나리오: 모음과 자음 개수 조건을 동시에 만족 다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

def longest_with_vowel_consonant(s: str, min_vowels: int, min_consonants: int) -> int:
    vowels = set('aeiouAEIOU')
    left = 0
    vowel_cnt = 0
    consonant_cnt = 0
    max_len = 0
    
    for right, ch in enumerate(s):
        if ch in vowels:
            vowel_cnt += 1
        elif ch.isalpha():
            consonant_cnt += 1
        
        while vowel_cnt >= min_vowels and consonant_cnt >= min_consonants:
            max_len = max(max_len, right - left + 1)
            
            left_char = s[left]
            if left_char in vowels:
                vowel_cnt -= 1
            elif left_char.isalpha():
                consonant_cnt -= 1
            left += 1
    
    return max_len

성능과 비교

시간 복잡도 비교

접근 방식시간 복잡도공간 복잡도비고
브루트포스 (이중 for)O(n²)O(1)모든 구간 확인
두 포인터O(n)O(1)정렬 필요 시 O(n log n)
슬라이딩 윈도우 + 해시맵O(n)O(k)k는 고유 문자 수
슬라이딩 윈도우 + 배열O(n)O(1)알파벳 한정
Deque 윈도우O(n)O(k)최대/최소 추적

벤치마크 예시

테스트: s = "abcabcbb" (길이 100만)

방법실행 시간메모리
브루트포스45s1MB
슬라이딩 윈도우 (해시맵)120ms5MB
슬라이딩 윈도우 (배열)80ms1MB
결론: 슬라이딩 윈도우로 375배 개선

실무 사례

사례 1: 로그 스트림 - 최근 k분 윈도우 합

시나리오: 실시간 로그에서 최근 5분간 에러 개수 추적

Python 구현

from collections import deque
from datetime import datetime, timedelta
class ErrorCounter:
    def __init__(self, window_minutes: int = 5):
        self.window = deque()
        self.window_minutes = window_minutes
    
    def add_error(self, timestamp: datetime):
        self.window.append(timestamp)
        cutoff = timestamp - timedelta(minutes=self.window_minutes)
        
        while self.window and self.window[0] < cutoff:
            self.window.popleft()
    
    def get_count(self) -> int:
        return len(self.window)
# 사용 예시
counter = ErrorCounter(window_minutes=5)
logs = [
    (datetime(2026, 3, 31, 10, 0), "error"),
    (datetime(2026, 3, 31, 10, 2), "error"),
    (datetime(2026, 3, 31, 10, 6), "error"),  # 첫 에러는 윈도우 밖
    (datetime(2026, 3, 31, 10, 7), "error"),
]
for ts, level in logs:
    if level == "error":
        counter.add_error(ts)
        print(f"{ts}: 최근 5분 에러 {counter.get_count()}개")
# 출력:
# 2026-03-31 10:00:00: 최근 5분 에러 1개
# 2026-03-31 10:02:00: 최근 5분 에러 2개
# 2026-03-31 10:06:00: 최근 5분 에러 2개 (10:00 제외)
# 2026-03-31 10:07:00: 최근 5분 에러 3개

시간 복잡도: 각 add_error는 amortized O(1)

사례 2: 네트워크 트래픽 - 대역폭 제한

시나리오: 최근 1초간 전송량이 10MB 초과 시 요청 거부

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

from collections import deque
import time
class BandwidthLimiter:
    def __init__(self, max_bytes: int = 10 * 1024 * 1024, window_sec: float = 1.0):
        self.max_bytes = max_bytes
        self.window_sec = window_sec
        self.requests = deque()  # (timestamp, bytes)
    
    def can_send(self, size_bytes: int) -> bool:
        now = time.time()
        cutoff = now - self.window_sec
        
        while self.requests and self.requests[0][0] < cutoff:
            self.requests.popleft()
        
        current_usage = sum(size for _, size in self.requests)
        
        if current_usage + size_bytes <= self.max_bytes:
            self.requests.append((now, size_bytes))
            return True
        return False
# 사용 예시
limiter = BandwidthLimiter(max_bytes=1000)
requests = [
    (100, "Request 1"),
    (200, "Request 2"),
    (300, "Request 3"),
    (500, "Request 4"),  # 거부됨 (100+200+300+500 > 1000)
]
for size, name in requests:
    if limiter.can_send(size):
        print(f"{name} ({size}B): 허용")
    else:
        print(f"{name} ({size}B): 거부 (대역폭 초과)")

사례 3: 문자열 검색 - Rabin-Karp 알고리즘

시나리오: 패턴 문자열을 텍스트에서 찾기 (해시 활용)

Python 구현

다음은 python를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class RabinKarp:
    def __init__(self, base: int = 256, mod: int = 10**9 + 7):
        self.base = base
        self.mod = mod
    
    def search(self, text: str, pattern: str) -> list[int]:
        n, m = len(text), len(pattern)
        if m > n:
            return []
        
        pattern_hash = 0
        window_hash = 0
        base_power = pow(self.base, m - 1, self.mod)
        
        for i in range(m):
            pattern_hash = (pattern_hash * self.base + ord(pattern[i])) % self.mod
            window_hash = (window_hash * self.base + ord(text[i])) % self.mod
        
        result = []
        
        for i in range(n - m + 1):
            if window_hash == pattern_hash:
                if text[i:i + m] == pattern:
                    result.append(i)
            
            if i < n - m:
                window_hash = (window_hash - ord(text[i]) * base_power) % self.mod
                window_hash = (window_hash * self.base + ord(text[i + m])) % self.mod
                window_hash = (window_hash + self.mod) % self.mod
        
        return result
# 사용 예시
rk = RabinKarp()
text = "ABABDABACDABABCABAB"
pattern = "ABABCABAB"
indices = rk.search(text, pattern)
print(f"패턴 발견 위치: {indices}")  # [10]

시간 복잡도: 평균 O(n + m), 최악 O(nm) (해시 충돌)

트러블슈팅

문제 1: while 조건을 헷갈린다

증상: 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 잘못된 예
for right in range(len(arr)):
    window_sum += arr[right]
    while window_sum > target:  # 조건 반대
        # ...

해결: 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 불변식 명시
# 불변식: window_sum >= target일 때 최소 길이 갱신
for right in range(len(arr)):
    window_sum += arr[right]
    
    # 조건 만족 시 left를 당기며 최소 길이 갱신
    while window_sum >= target:
        min_len = min(min_len, right - left + 1)
        window_sum -= arr[left]
        left += 1

: 주석으로 불변식을 명시하면 실수 감소

문제 2: 오프바이원 (Off-by-One)

증상:

# 길이 계산 실수
length = right - left  # 잘못됨

해결: 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# [left, right] 구간 길이
length = right - left + 1
# 테스트 케이스
# left=0, right=0 → 길이 1 (맞음)
# left=0, right=2 → 길이 3 (맞음)

문제 3: 시간 초과 (TLE)

증상: 윈도우 내부에서 무거운 연산 반복 아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 잘못된 예
for right in range(len(arr)):
    # 매번 윈도우 전체를 순회 → O(n²)
    if is_valid(arr[left:right+1]):
        ans = max(ans, right - left + 1)

해결: 증분 갱신 아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 올바른 예
for right in range(len(arr)):
    # O(1) 갱신
    window_state.add(arr[right])
    
    while not window_state.is_valid():
        window_state.remove(arr[left])
        left += 1
    
    ans = max(ans, right - left + 1)

문제 4: 음수 처리

증상: 합이 target 이상인 최소 길이 (음수 포함)

# 두 포인터로 불가능
# 예: nums = [2, -1, 2], target = 3
# left를 당겨도 합이 증가할 수 있음

해결: 음수가 있으면 두 포인터 불가 → DP 또는 Prefix Sum + 이분 탐색

마무리

LeetCode 두 포인터 슬라이딩 윈도우는 템플릿을 외운 뒤 불변식만 문제마다 바꾸는 훈련이 효율적입니다.

패턴 선택 가이드

문제 유형패턴예시
정렬 배열에서 합 찾기대칭형 두 포인터Two Sum II
중복 제거 (in-place)동시 전진 두 포인터Remove Duplicates
고정 길이 구간 최대/평균고정 윈도우Maximum Average Subarray
조건 만족 최소/최대 길이가변 윈도우Minimum Window Substring
윈도우 내 최대/최소 추적Deque 윈도우Sliding Window Maximum

다음 단계

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3