diff --git a/best-time-to-buy-and-sell-stock/ICE0208.java b/best-time-to-buy-and-sell-stock/ICE0208.java new file mode 100644 index 0000000000..552000a6ee --- /dev/null +++ b/best-time-to-buy-and-sell-stock/ICE0208.java @@ -0,0 +1,31 @@ +class Solution { + /** + * 오늘 주식을 판매한다고 가정하면, + * 이전 날짜 중 가장 낮은 가격에 구매했을 때 최대 이익을 얻을 수 있다. + * 배열을 순회하면서 최저 가격과 최대 이익을 계속 갱신한다. + * + * 시간 복잡도: O(n) + * 공간 복잡도: O(1) + */ + public int maxProfit(int[] prices) { + // 현재까지 확인한 날짜 중 가장 낮은 주가 + int minPrice = prices[0]; + + // 현재까지 얻을 수 있는 최대 이익 + int maxProfit = 0; + + // 첫 번째 가격은 minPrice로 사용했으므로 두 번째 가격부터 확인한다. + for (int i = 1; i < prices.length; i++) { + int currentPrice = prices[i]; + int currentProfit = currentPrice - minPrice; + + // 이전 최저가에 구매하고 현재 가격에 판매했을 때의 이익을 비교한다. + maxProfit = Math.max(maxProfit, currentProfit); + + // 이후 날짜의 계산을 위해 지금까지의 최저가를 갱신한다. + minPrice = Math.min(minPrice, currentPrice); + } + + return maxProfit; + } +} diff --git a/encode-and-decode-strings/ICE0208.py b/encode-and-decode-strings/ICE0208.py new file mode 100644 index 0000000000..4c6ad96bec --- /dev/null +++ b/encode-and-decode-strings/ICE0208.py @@ -0,0 +1,49 @@ +from typing import List + + +class Solution: + """ + 각 문자열을 '문자열 길이#문자열' 형식으로 인코딩한다. + + 예: + ["Hello", "World", ""] + -> "5#Hello5#World0#" + + 시간 복잡도: O(n) + 공간 복잡도: O(n) + + n은 모든 문자열 길이의 합이다. + """ + + def encode(self, strs: List[str]) -> str: + encoded = [] + + for word in strs: + encoded.append(f"{len(word)}#{word}") + + return "".join(encoded) + + def decode(self, encoded_string: str) -> List[str]: + decoded = [] + index = 0 + + while index < len(encoded_string): + delimiter_index = index + + # 문자열 길이와 실제 문자열을 나누는 '#'을 찾는다. + while encoded_string[delimiter_index] != "#": + delimiter_index += 1 + + word_length = int( + encoded_string[index:delimiter_index] + ) + + word_start = delimiter_index + 1 + word_end = word_start + word_length + + decoded.append(encoded_string[word_start:word_end]) + + # 다음 문자열의 길이가 시작되는 위치로 이동한다. + index = word_end + + return decoded diff --git a/group-anagrams/ICE0208.java b/group-anagrams/ICE0208.java new file mode 100644 index 0000000000..56a36ff3f1 --- /dev/null +++ b/group-anagrams/ICE0208.java @@ -0,0 +1,33 @@ +class Solution { + /** + * 각 문자열의 알파벳 등장 횟수를 키로 사용해 + * 같은 애너그램끼리 그룹화한다. + * + * 시간 복잡도: O(S) + * 공간 복잡도: O(n) + * + * S: 모든 문자열 길이의 합 + * n: 문자열의 개수 + */ + public List> groupAnagrams(String[] strs) { + Map> groups = new HashMap<>(); + + for (String word : strs) { + int[] frequency = new int[26]; + + // 알파벳별 등장 횟수를 계산한다. + for (int i = 0; i < word.length(); i++) { + frequency[word.charAt(i) - 'a']++; + } + + // 빈도 배열을 같은 내용끼리 비교할 수 있는 키로 변환한다. + String key = Arrays.toString(frequency); + + // 같은 키를 가진 문자열을 동일한 그룹에 추가한다. + groups.computeIfAbsent(key, ignored -> new ArrayList<>()) + .add(word); + } + + return new ArrayList<>(groups.values()); + } +} diff --git a/implement-trie-prefix-tree/ICE0208.py b/implement-trie-prefix-tree/ICE0208.py new file mode 100644 index 0000000000..849a2447c6 --- /dev/null +++ b/implement-trie-prefix-tree/ICE0208.py @@ -0,0 +1,51 @@ +class TrieNode: + def __init__(self): + # 다음 문자로 연결되는 자식 노드 + self.children = {} + + # 현재 노드에서 하나의 완성된 단어가 끝나는지 표시 + self.is_end_of_word = False + + +class Trie: + def __init__(self): + # 모든 단어 탐색이 시작되는 최상위 노드 + self.root = TrieNode() + + def insert(self, word: str) -> None: + current_node = self.root + + # 단어의 각 문자를 따라가며 경로를 생성한다. + for char in word: + if char not in current_node.children: + current_node.children[char] = TrieNode() + + current_node = current_node.children[char] + + # 마지막 노드에 단어의 끝임을 표시한다. + current_node.is_end_of_word = True + + def search(self, word: str) -> bool: + last_node = self._find_last_node(word) + + # 경로가 존재하고, 마지막 노드에서 단어가 끝나야 한다. + return ( + last_node is not None + and last_node.is_end_of_word + ) + + def startsWith(self, prefix: str) -> bool: + # 접두사는 해당 경로가 존재하기만 하면 된다. + return self._find_last_node(prefix) is not None + + def _find_last_node(self, text: str): + current_node = self.root + + # 문자열의 각 문자를 따라 마지막 노드까지 이동한다. + for char in text: + if char not in current_node.children: + return None + + current_node = current_node.children[char] + + return current_node diff --git a/word-break/ICE0208.py b/word-break/ICE0208.py new file mode 100644 index 0000000000..64104bf2a8 --- /dev/null +++ b/word-break/ICE0208.py @@ -0,0 +1,26 @@ +from functools import cache + + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + word_set = set(wordDict) + + @cache + def finding(start): + # 문자열 끝까지 단어들로 나누는 데 성공한 경우 + if start == len(s): + return True + + # start부터 시작하는 모든 부분 문자열을 확인한다. + for end in range(start + 1, len(s) + 1): + current_word = s[start:end] + + # 현재 단어가 사전에 있고, + # 나머지 문자열도 나눌 수 있다면 바로 종료한다. + if current_word in word_set and finding(end): + return True + + # 어떤 방식으로도 나눌 수 없는 경우 + return False + + return finding(0)