diff --git a/best-time-to-buy-and-sell-stock/socow.py b/best-time-to-buy-and-sell-stock/socow.py new file mode 100644 index 000000000..364b8ca50 --- /dev/null +++ b/best-time-to-buy-and-sell-stock/socow.py @@ -0,0 +1,68 @@ +""" +121. Best Time to Buy and Sell Stock +https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ + +문제: 주식 가격 배열이 주어질 때, 한 번 사고 한 번 팔아서 얻을 수 있는 최대 이익을 구하라. + (매수는 매도보다 앞선 날짜여야 함) + +예시: + Input: prices = [7, 1, 5, 3, 6, 4] + Output: 5 (1일에 사서 4일에 팔면 6-1=5) +""" + +from typing import List + + +# ============================================================================= +# 풀이 1: 브루트 포스 (Brute Force) +# ============================================================================= +# 아이디어: 모든 (매수일, 매도일) 조합을 확인하여 최대 이익을 찾는다. +# 시간 복잡도: O(n²) - 이중 반복문 +# 공간 복잡도: O(1) +# 결과: Time Limit Exceeded (시간 초과) +# ============================================================================= +class Solution: + def maxProfit(self, prices: List[int]) -> int: + result = 0 + + # i: 매수일 (0 ~ n-2) + for i in range(len(prices) - 1): + # r: 매도일 (i+1 ~ n-1), 매수일 이후여야 함 + for r in range(i + 1, len(prices)): + # 현재 조합의 이익 = 매도가 - 매수가 + profit = prices[r] - prices[i] + result = max(result, profit) + + return result + + +# ============================================================================= +# 풀이 2: 한 번 순회 (One Pass) - 최적화 +# ============================================================================= +# 아이디어: 배열을 순회하면서 "지금까지의 최소 매수가"를 추적한다. +# 각 날짜에서 현재 가격으로 팔았을 때의 이익을 계산하고 최대값을 갱신한다. +# +# 핵심 통찰: +# - 특정 날짜에 팔 때 최대 이익 = 현재가격 - (이전까지의 최소가격) +# - 따라서 최소 가격만 추적하면 O(n)에 해결 가능 +# +# 시간 복잡도: O(n) - 한 번 순회 +# 공간 복잡도: O(1) +# ============================================================================= +class Solution: + def maxProfit(self, prices: List[int]) -> int: + max_profit = 0 # 최대 이익 + min_price = prices[0] # 지금까지의 최소 매수가 + + # 1일차부터 순회 (0일차는 min_price 초기값) + for price in prices[1:]: + # 오늘 팔았을 때의 이익 계산 + profit = price - min_price + # 최대 이익 갱신 + max_profit = max(max_profit, profit) + # 최소 매수가 갱신 (더 싼 날이 있으면 업데이트) + min_price = min(min_price, price) + + return max_profit + + diff --git a/group-anagrams/socow.py b/group-anagrams/socow.py new file mode 100644 index 000000000..4213e2453 --- /dev/null +++ b/group-anagrams/socow.py @@ -0,0 +1,46 @@ +""" +49. Group Anagrams +https://leetcode.com/problems/group-anagrams/ + +문제: 문자열 배열이 주어질 때, 애너그램끼리 그룹으로 묶어서 반환하라. + 애너그램 = 같은 문자들로 이루어진 단어 (순서만 다름) + +예시: + Input: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] + Output: [["bat"], ["nat", "tan"], ["ate", "eat", "tea"]] + + "eat", "tea", "ate" → 모두 a, e, t로 구성 → 같은 그룹 + "tan", "nat" → 모두 a, n, t로 구성 → 같은 그룹 + "bat" → 혼자 → 별도 그룹 +""" + + +# ============================================================================= +# 풀이 1: 정렬(Sorted)을 키로 사용 +# ============================================================================= +# 아이디어: 애너그램은 정렬하면 같은 문자열이 된다! +# "eat" → "aet", "tea" → "aet", "ate" → "aet" +# 정렬된 문자열을 딕셔너리의 키로 사용하여 그룹화 +# +# 시간 복잡도: O(n * k log k) +# - n: 문자열 개수 +# - k: 가장 긴 문자열의 길이 +# - 각 문자열을 정렬하는데 O(k log k) +# +# 공간 복잡도: O(n * k) - 모든 문자열을 딕셔너리에 저장 +# ============================================================================= +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + anagram_map = {} # 일반 딕셔너리 + + for word in strs: + sorted_key = "".join(sorted(word)) + + # 키가 없으면 먼저 빈 리스트 생성 + if sorted_key not in anagram_map: + anagram_map[sorted_key] = [] + + # 그 다음 append + anagram_map[sorted_key].append(word) + + return list(anagram_map.values())