Skip to content
Published on

FAANG 코딩 인터뷰 완전 정복: 배열과 문자열 핵심 패턴

Authors

FAANG 코딩 인터뷰 완전 정복: 배열과 문자열 핵심 패턴

FAANG(Facebook/Meta, Apple, Amazon, Netflix, Google) 코딩 인터뷰에서 배열과 문자열 문제는 전체 출제 비중의 40% 이상을 차지합니다. 이 글에서는 실제 인터뷰에서 자주 등장하는 6가지 핵심 패턴을 실전 코드와 함께 완전히 정복합니다.


1. Two Pointers 패턴

개념 및 언제 사용하는가

Two Pointers 패턴은 정렬된 배열이나 연결 리스트에서 두 개의 포인터를 사용해 선형 시간에 문제를 해결하는 기법입니다.

사용 조건:

  • 정렬된 배열에서 두 원소의 합/차를 구하는 경우
  • 배열의 양 끝에서 조건을 만족하는 쌍을 찾는 경우
  • 중복 제거나 파티셔닝이 필요한 경우

핵심 아이디어: O(n^2) 브루트 포스를 O(n)으로 줄일 수 있습니다.


문제 1: Two Sum II (LeetCode 167)

정렬된 배열에서 두 수의 합이 target이 되는 인덱스를 찾아라.

def twoSum(numbers: list[int], target: int) -> list[int]:
    left, right = 0, len(numbers) - 1

    while left < right:
        current_sum = numbers[left] + numbers[right]
        if current_sum == target:
            return [left + 1, right + 1]  # 1-indexed
        elif current_sum < target:
            left += 1
        else:
            right -= 1

    return []  # 문제 조건상 항상 해가 존재

# 예시
# Input: numbers = [2,7,11,15], target = 9
# Output: [1,2]

시간 복잡도: O(n) - 각 포인터가 최대 n번 이동 공간 복잡도: O(1) - 추가 공간 불필요

FAANG 팁: 정렬되지 않은 배열이라면 HashMap을 사용한 O(n) 풀이를 먼저 제시하고, 정렬된 경우 Two Pointers가 더 공간 효율적임을 설명하세요.


문제 2: 3Sum (LeetCode 15)

배열에서 합이 0인 세 원소를 모두 찾아라 (중복 없이).

def threeSum(nums: list[int]) -> list[list[int]]:
    nums.sort()
    result = []

    for i in range(len(nums) - 2):
        # 중복 건너뛰기
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, len(nums) - 1

        while left < right:
            total = nums[i] + nums[left] + nums[right]

            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                # 중복 건너뛰기
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

# 예시
# Input: nums = [-1, 0, 1, 2, -1, -4]
# Output: [[-1, -1, 2], [-1, 0, 1]]

시간 복잡도: O(n^2) - 정렬 O(n log n) + 이중 루프 O(n^2) 공간 복잡도: O(1) ~ O(n) (정렬 알고리즘에 따라)

FAANG 팁: Meta 인터뷰에서 자주 출제됩니다. 중복 처리 로직을 빠뜨리지 않도록 주의하세요.


문제 3: Container With Most Water (LeetCode 11)

높이 배열이 주어질 때 가장 많은 물을 담을 수 있는 컨테이너의 용량을 구하라.

def maxArea(height: list[int]) -> int:
    left, right = 0, len(height) - 1
    max_water = 0

    while left < right:
        # 현재 물의 양 = 너비 * 더 낮은 높이
        width = right - left
        current_water = width * min(height[left], height[right])
        max_water = max(max_water, current_water)

        # 더 낮은 쪽 포인터를 이동 (더 높은 값을 찾기 위해)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_water

# 예시
# Input: height = [1,8,6,2,5,4,8,3,7]
# Output: 49

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

왜 이 방식이 최적인가? 더 낮은 높이의 벽을 이동하는 이유는, 더 높은 벽을 이동하면 너비도 줄고 높이도 줄거나 같아서 절대 더 많은 물을 담을 수 없기 때문입니다.


문제 4: Trapping Rain Water (LeetCode 42)

고난도 문제로 빗물을 가두는 총량을 구하라.

def trap(height: list[int]) -> int:
    if not height:
        return 0

    left, right = 0, len(height) - 1
    left_max, right_max = height[left], height[right]
    water = 0

    while left < right:
        if left_max <= right_max:
            left += 1
            left_max = max(left_max, height[left])
            # 현재 위치에서 담을 수 있는 물 = 왼쪽 최대 - 현재 높이
            water += left_max - height[left]
        else:
            right -= 1
            right_max = max(right_max, height[right])
            water += right_max - height[right]

    return water

# 예시
# Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
# Output: 6

시간 복잡도: O(n) 공간 복잡도: O(1) - prefix max 배열 없이 투 포인터로 해결

FAANG 팁: Google, Amazon 인터뷰 단골 문제입니다. DP 방식(O(n) space)도 알아두고, 투 포인터가 왜 더 효율적인지 설명할 수 있어야 합니다.


2. Sliding Window 패턴

개념

연속된 부분 배열/문자열에서 최적해를 찾는 패턴입니다. 윈도우를 슬라이딩하며 전체를 한 번만 순회합니다.

Fixed Window: 윈도우 크기가 고정된 경우 Variable Window: 조건에 따라 윈도우 크기가 변하는 경우


문제 5: Longest Substring Without Repeating Characters (LeetCode 3)

def lengthOfLongestSubstring(s: str) -> int:
    char_set = set()
    left = 0
    max_length = 0

    for right in range(len(s)):
        # 중복 문자가 있으면 왼쪽 포인터 이동
        while s[right] in char_set:
            char_set.remove(s[left])
            left += 1

        char_set.add(s[right])
        max_length = max(max_length, right - left + 1)

    return max_length

# 최적화 버전 (HashMap 사용)
def lengthOfLongestSubstringOptimized(s: str) -> int:
    char_index = {}
    left = 0
    max_length = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1

        char_index[char] = right
        max_length = max(max_length, right - left + 1)

    return max_length

# 예시
# Input: "abcabcbb"
# Output: 3 ("abc")

시간 복잡도: O(n) 공간 복잡도: O(min(n, charset_size))


문제 6: Minimum Window Substring (LeetCode 76)

문자열 s에서 t의 모든 문자를 포함하는 최소 윈도우를 찾아라.

from collections import Counter

def minWindow(s: str, t: str) -> str:
    if not t or not s:
        return ""

    need = Counter(t)
    missing = len(t)  # 아직 포함하지 못한 문자 수

    start = 0
    best_start = 0
    best_end = float('inf')

    left = 0
    for right, char in enumerate(s):
        # 필요한 문자라면 missing 감소
        if need[char] > 0:
            missing -= 1
        need[char] -= 1

        # 모든 문자를 포함했을 때 윈도우 최소화
        if missing == 0:
            # 왼쪽에서 불필요한 문자 제거
            while need[s[left]] < 0:
                need[s[left]] += 1
                left += 1

            # 현재 윈도우가 더 작으면 업데이트
            if right - left < best_end - best_start:
                best_start = left
                best_end = right

            # 윈도우 축소 (다음 더 짧은 윈도우 탐색)
            need[s[left]] += 1
            missing += 1
            left += 1

    if best_end == float('inf'):
        return ""
    return s[best_start:best_end + 1]

# 예시
# Input: s = "ADOBECODEBANC", t = "ABC"
# Output: "BANC"

시간 복잡도: O(|s| + |t|) 공간 복잡도: O(|s| + |t|)


문제 7: Sliding Window Maximum (LeetCode 239)

크기 k인 슬라이딩 윈도우에서 각 위치의 최대값을 구하라.

from collections import deque

def maxSlidingWindow(nums: list[int], k: int) -> list[int]:
    dq = deque()  # 인덱스를 저장 (값의 감소 순서 유지)
    result = []

    for i, num in enumerate(nums):
        # 윈도우 밖으로 나간 인덱스 제거
        if dq and dq[0] < i - k + 1:
            dq.popleft()

        # 현재 값보다 작은 원소는 유용하지 않으므로 제거
        while dq and nums[dq[-1]] < num:
            dq.pop()

        dq.append(i)

        # 윈도우가 완성되면 결과 추가
        if i >= k - 1:
            result.append(nums[dq[0]])

    return result

# 예시
# Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
# Output: [3,3,5,5,6,7]

시간 복잡도: O(n) - 각 원소가 deque에 한 번씩 추가/삭제 공간 복잡도: O(k)


문제 8: Fruit Into Baskets (LeetCode 904)

최대 2종류의 과일만 담을 수 있는 바구니로 연속해서 따는 과일의 최대 개수를 구하라.

from collections import defaultdict

def totalFruit(fruits: list[int]) -> int:
    basket = defaultdict(int)
    left = 0
    max_fruits = 0

    for right, fruit in enumerate(fruits):
        basket[fruit] += 1

        # 3종류 이상이면 왼쪽 포인터 이동
        while len(basket) > 2:
            basket[fruits[left]] -= 1
            if basket[fruits[left]] == 0:
                del basket[fruits[left]]
            left += 1

        max_fruits = max(max_fruits, right - left + 1)

    return max_fruits

# 예시
# Input: fruits = [1,2,1,2,3]
# Output: 4 ([1,2,1,2])

시간 복잡도: O(n) 공간 복잡도: O(1) - 최대 3가지 과일 종류만 저장


3. Binary Search 패턴

개념

정렬된 배열에서 O(log n) 시간에 원소를 탐색합니다. 표준 형태 외에도 다양한 변형이 FAANG 인터뷰에 등장합니다.

표준 Binary Search 템플릿:

def binarySearch(nums: list[int], target: int) -> int:
    left, right = 0, len(nums) - 1

    while left <= right:
        mid = left + (right - left) // 2  # 오버플로우 방지

        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1

    return -1

문제 9: Search in Rotated Sorted Array (LeetCode 33)

회전된 정렬 배열에서 타겟을 찾아라.

def search(nums: list[int], target: int) -> int:
    left, right = 0, len(nums) - 1

    while left <= right:
        mid = left + (right - left) // 2

        if nums[mid] == target:
            return mid

        # 왼쪽 절반이 정렬된 경우
        if nums[left] <= nums[mid]:
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        # 오른쪽 절반이 정렬된 경우
        else:
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1

    return -1

# 예시
# Input: nums = [4,5,6,7,0,1,2], target = 0
# Output: 4

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


문제 10: Find Minimum in Rotated Sorted Array (LeetCode 153)

def findMin(nums: list[int]) -> int:
    left, right = 0, len(nums) - 1

    while left < right:
        mid = left + (right - left) // 2

        # mid가 right보다 크면 최소값은 오른쪽 절반에 있음
        if nums[mid] > nums[right]:
            left = mid + 1
        else:
            right = mid

    return nums[left]

# 예시
# Input: nums = [3,4,5,1,2]
# Output: 1

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


문제 11: Median of Two Sorted Arrays (LeetCode 4) - 고난도

def findMedianSortedArrays(nums1: list[int], nums2: list[int]) -> float:
    # nums1이 더 짧은 배열이 되도록 보장
    if len(nums1) > len(nums2):
        nums1, nums2 = nums2, nums1

    m, n = len(nums1), len(nums2)
    half_len = (m + n + 1) // 2

    left, right = 0, m

    while left <= right:
        i = (left + right) // 2  # nums1의 파티션
        j = half_len - i          # nums2의 파티션

        # i가 너무 작으면 오른쪽으로
        if i < m and nums1[i] < nums2[j - 1]:
            left = i + 1
        # i가 너무 크면 왼쪽으로
        elif i > 0 and nums1[i - 1] > nums2[j]:
            right = i - 1
        else:
            # 올바른 파티션 찾음
            if i == 0:
                max_left = nums2[j - 1]
            elif j == 0:
                max_left = nums1[i - 1]
            else:
                max_left = max(nums1[i - 1], nums2[j - 1])

            if (m + n) % 2 == 1:
                return float(max_left)

            if i == m:
                min_right = nums2[j]
            elif j == n:
                min_right = nums1[i]
            else:
                min_right = min(nums1[i], nums2[j])

            return (max_left + min_right) / 2.0

# 예시
# Input: nums1 = [1,3], nums2 = [2]
# Output: 2.0

시간 복잡도: O(log(min(m, n))) 공간 복잡도: O(1)


문제 12: Search a 2D Matrix (LeetCode 74)

def searchMatrix(matrix: list[list[int]], target: int) -> bool:
    if not matrix or not matrix[0]:
        return False

    rows, cols = len(matrix), len(matrix[0])
    left, right = 0, rows * cols - 1

    while left <= right:
        mid = left + (right - left) // 2
        # 1D 인덱스를 2D 좌표로 변환
        mid_val = matrix[mid // cols][mid % cols]

        if mid_val == target:
            return True
        elif mid_val < target:
            left = mid + 1
        else:
            right = mid - 1

    return False

# 예시
# Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
# Output: True

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


4. Prefix Sum & Hash Map 패턴

개념

Prefix Sum은 누적합을 미리 계산해 구간 합 쿼리를 O(1)에 처리합니다. Hash Map과 결합하면 특정 조건을 만족하는 구간을 효율적으로 찾을 수 있습니다.


문제 13: Subarray Sum Equals K (LeetCode 560)

합이 k인 연속 부분 배열의 개수를 구하라.

from collections import defaultdict

def subarraySum(nums: list[int], k: int) -> int:
    # prefix_sum_count[s] = prefix sum이 s인 경우의 수
    prefix_sum_count = defaultdict(int)
    prefix_sum_count[0] = 1  # 빈 배열의 prefix sum = 0

    current_sum = 0
    count = 0

    for num in nums:
        current_sum += num
        # current_sum - k가 이전에 나온 prefix sum이면 해당 구간의 합 = k
        count += prefix_sum_count[current_sum - k]
        prefix_sum_count[current_sum] += 1

    return count

# 예시
# Input: nums = [1,1,1], k = 2
# Output: 2

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


문제 14: Longest Consecutive Sequence (LeetCode 128)

정렬되지 않은 배열에서 가장 긴 연속 수열의 길이를 구하라 (O(n) 요구).

def longestConsecutive(nums: list[int]) -> int:
    num_set = set(nums)
    max_length = 0

    for num in num_set:
        # 수열의 시작점인 경우만 처리 (num-1이 없는 경우)
        if num - 1 not in num_set:
            current = num
            length = 1

            while current + 1 in num_set:
                current += 1
                length += 1

            max_length = max(max_length, length)

    return max_length

# 예시
# Input: nums = [100,4,200,1,3,2]
# Output: 4 ([1,2,3,4])

시간 복잡도: O(n) - 각 원소는 최대 두 번 방문 공간 복잡도: O(n)


문제 15: Group Anagrams (LeetCode 49)

문자열 배열을 애너그램끼리 묶어라.

from collections import defaultdict

def groupAnagrams(strs: list[str]) -> list[list[str]]:
    anagram_map = defaultdict(list)

    for s in strs:
        # 정렬된 문자열을 키로 사용
        key = tuple(sorted(s))
        anagram_map[key].append(s)

    return list(anagram_map.values())

# 카운터를 키로 사용하는 최적화 버전
def groupAnagramsOptimized(strs: list[str]) -> list[list[str]]:
    anagram_map = defaultdict(list)

    for s in strs:
        # 26개 알파벳 카운트를 키로 사용
        count = [0] * 26
        for c in s:
            count[ord(c) - ord('a')] += 1
        anagram_map[tuple(count)].append(s)

    return list(anagram_map.values())

# 예시
# Input: strs = ["eat","tea","tan","ate","nat","bat"]
# Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

시간 복잡도: O(n _ k) - n은 문자열 수, k는 최대 문자열 길이 공간 복잡도: O(n _ k)


문제 16: Top K Frequent Elements (LeetCode 347)

from collections import Counter
import heapq

def topKFrequent(nums: list[int], k: int) -> list[int]:
    # 방법 1: 힙 사용 O(n log k)
    count = Counter(nums)
    return heapq.nlargest(k, count.keys(), key=count.get)

def topKFrequentBucketSort(nums: list[int], k: int) -> list[int]:
    # 방법 2: 버킷 정렬 O(n)
    count = Counter(nums)

    # 빈도를 인덱스로 사용하는 버킷
    bucket = [[] for _ in range(len(nums) + 1)]
    for num, freq in count.items():
        bucket[freq].append(num)

    result = []
    for freq in range(len(bucket) - 1, 0, -1):
        for num in bucket[freq]:
            result.append(num)
            if len(result) == k:
                return result

    return result

# 예시
# Input: nums = [1,1,1,2,2,3], k = 2
# Output: [1,2]

시간 복잡도: O(n) with Bucket Sort 공간 복잡도: O(n)


5. FAANG 기업별 배열/문자열 출제 경향

Google

Google 인터뷰는 Arrays와 String Manipulation에 집중합니다.

  • 자주 출제: Two Sum 변형, 문자열 파싱, 2D 배열 탐색
  • 핵심 스킬: 엣지 케이스 처리, 코드 명확성
  • 예시 문제: Longest Substring with At Most K Distinct Characters, Minimum Number of Arrows to Burst Balloons

Meta (Facebook)

Meta는 Sliding Window와 Two Pointer 패턴을 선호합니다.

  • 자주 출제: 3Sum, Valid Palindrome II, Move Zeroes
  • 핵심 스킬: 최적화 이유 설명, 코드 간결성
  • 예시 문제: Subarray Product Less Than K, Permutation in String

Amazon

Amazon은 Hash Table과 Sorting 기반 문제를 선호합니다.

  • 자주 출제: Two Sum, Group Anagrams, Top K Frequent
  • 핵심 스킬: 실용적인 해결책, LP(리더십 원칙) 연결
  • 예시 문제: K Closest Points to Origin, Sort Colors

Microsoft

Microsoft는 Binary Search 변형과 배열 조작 문제를 자주 냅니다.

  • 자주 출제: Find Peak Element, Search a 2D Matrix
  • 핵심 스킬: 엣지 케이스, 버그 없는 코드
  • 예시 문제: Spiral Matrix, Rotate Image

6. 인터뷰 실전 팁

문제 접근 프레임워크 (45분 기준)

1단계 - Clarify (5분)

면접관에게 반드시 확인할 사항들:

  • 입력 범위와 타입 (음수 가능? 중복 있음?)
  • 출력 형식 (인덱스? 값? 개수?)
  • 엣지 케이스 (빈 배열, 단일 원소, 모두 같은 값)
  • 최적화 목표 (시간 vs 공간)

2단계 - Brute Force (5분)

먼저 O(n^2) 또는 O(n^3) 브루트 포스를 설명합니다. 이것이 베이스라인이 됩니다.

3단계 - Optimize (10분)

패턴 인식: Two Pointers? Sliding Window? Hash Map? 면접관과 트레이드오프를 논의합니다.

4단계 - Code (15분)

깔끔하게 코딩합니다. 변수명은 명확하게, 주석은 핵심만:

def solve(nums, target):
    # 투 포인터 초기화
    left, right = 0, len(nums) - 1

    while left < right:
        # ... 로직
        pass

5단계 - Test (10분)

직접 테스트 케이스를 만들어 검증합니다:

  • 정상 케이스
  • 엣지 케이스 (빈 배열, 길이 1)
  • 최악의 경우

엣지 케이스 체크리스트

배열/문자열 문제에서 반드시 확인:

  • 빈 입력 ([], "")
  • 단일 원소 ([5], "a")
  • 모든 원소가 같은 값 ([3,3,3,3])
  • 이미 정렬된 경우
  • 역순 정렬된 경우
  • 오버플로우 가능성 (int 범위 초과)
  • 음수 포함 여부

시간 분배 전략

단계시간핵심 포인트
Clarify5분면접관에게 질문하며 요구사항 확인
Brute Force5분단순한 해법 먼저 제시
Optimize10분패턴 파악, 복잡도 개선
Coding15분깔끔하고 버그 없이
Testing10분직접 트레이스, 엣지 케이스

7. 연습 문제 퀴즈

퀴즈 1: Two Pointers로 정렬된 배열에서 중복 제거하기

정렬된 배열 nums = [0,0,1,1,1,2,2,3,3,4]에서 in-place로 중복을 제거하고 고유 원소의 수를 반환하라. 추가 공간은 O(1)만 사용 가능.

정답: 5 (배열은 [0,1,2,3,4,...])

설명: Two Pointers를 사용합니다. 느린 포인터(slow)는 다음에 쓸 위치를, 빠른 포인터(fast)는 탐색 위치를 가리킵니다. nums[fast] != nums[slow]이면 slow를 증가시키고 값을 복사합니다.

def removeDuplicates(nums):
    if not nums:
        return 0
    slow = 0
    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1
퀴즈 2: Sliding Window로 최대 연속 1의 개수 구하기

이진 배열 nums = [1,1,0,0,1,1,1,0,1]에서 최대 k=1개의 0을 1로 바꿀 수 있을 때 가장 긴 연속 1의 부분 배열 길이를 구하라.

정답: 6

설명: Variable Sliding Window를 사용합니다. 윈도우 내 0의 개수가 k를 초과하면 왼쪽 포인터를 이동합니다.

def longestOnes(nums, k):
    left = 0
    zeros = 0
    max_len = 0
    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1
        while zeros > k:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        max_len = max(max_len, right - left + 1)
    return max_len
퀴즈 3: Binary Search 응용 - 경계값 찾기

정렬된 배열 nums = [5,7,7,8,8,10]에서 target=8의 시작 인덱스와 끝 인덱스를 구하라.

정답: [3, 4]

설명: Binary Search를 두 번 실행합니다. 첫 번째는 가장 왼쪽 인덱스(leftmost), 두 번째는 가장 오른쪽 인덱스(rightmost)를 찾습니다.

def searchRange(nums, target):
    def findLeft(nums, target):
        left, right = 0, len(nums) - 1
        idx = -1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                idx = mid
                right = mid - 1  # 왼쪽으로 계속 탐색
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return idx

    def findRight(nums, target):
        left, right = 0, len(nums) - 1
        idx = -1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                idx = mid
                left = mid + 1  # 오른쪽으로 계속 탐색
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return idx

    return [findLeft(nums, target), findRight(nums, target)]
퀴즈 4: Prefix Sum으로 짝수 길이 구간의 합이 같은 경우 세기

배열 [1, -1, 1, -1, 1]에서 합이 0인 연속 부분 배열의 개수는?

정답: 4

설명: Prefix Sum + HashMap을 사용합니다. prefix sum이 같은 경우가 m번 나타나면 C(m,2)개의 구간이 합이 0입니다. prefix_sum_count를 이용해 count += prefix_sum_count[current_sum]으로 계산합니다.

from collections import defaultdict
def subarraySum(nums, k=0):
    prefix_sum_count = defaultdict(int)
    prefix_sum_count[0] = 1
    current_sum = 0
    count = 0
    for num in nums:
        current_sum += num
        count += prefix_sum_count[current_sum - k]
        prefix_sum_count[current_sum] += 1
    return count
# 결과: 4
퀴즈 5: 종합 문제 - Product of Array Except Self

배열 nums = [1,2,3,4]에서 각 위치에 대해 자기 자신을 제외한 나머지 원소의 곱을 구하라. 나눗셈 사용 금지, O(n) 시간, O(1) 추가 공간.

정답: [24, 12, 8, 6]

설명: 왼쪽 누적곱(prefix product)과 오른쪽 누적곱(suffix product)을 활용합니다. 출력 배열 자체를 재활용해 공간을 절약합니다.

def productExceptSelf(nums):
    n = len(nums)
    result = [1] * n

    # 왼쪽 누적곱
    prefix = 1
    for i in range(n):
        result[i] = prefix
        prefix *= nums[i]

    # 오른쪽 누적곱을 곱하기
    suffix = 1
    for i in range(n - 1, -1, -1):
        result[i] *= suffix
        suffix *= nums[i]

    return result

마무리

배열과 문자열 문제는 FAANG 인터뷰의 핵심입니다. 이 글에서 다룬 패턴들을 완전히 내재화하면 대부분의 문제를 빠르게 인식하고 최적 해법으로 접근할 수 있습니다.

다음 학습 단계:

  1. LeetCode에서 각 패턴별 문제 20개씩 풀기
  2. 시간을 재며 45분 안에 해결하는 연습
  3. 코드 리뷰: 다른 사람의 풀이와 비교
  4. 면접관 역할로 직접 문제 출제해보기

다음 편에서는 트리와 그래프 패턴을 다룹니다.