Skip to content

Commit 4cddfb0

Browse files
authored
Merge branch 'DaleStudy:main' into main
2 parents e9eb1c4 + f471c4a commit 4cddfb0

7 files changed

Lines changed: 289 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// naive하게 풀면, time limit 초과함
2+
// O(n^2) 풀이가 되기 때문..
3+
const maxProfit_naive = function (prices) {
4+
let profit = 0;
5+
6+
for (let i = 0; i < prices.length - 1; i++) {
7+
const buy = prices[i];
8+
for (let j = i + 1; j < prices.length; j++) {
9+
const newProfit = prices[j] - buy;
10+
if (newProfit > profit) {
11+
profit = newProfit;
12+
}
13+
}
14+
}
15+
16+
return profit;
17+
};
18+
19+
// 투포인터 사용하여 풀기
20+
// tc: O(n)
21+
// sc: O(1)
22+
const maxProfit = function (prices) {
23+
let buyIdx = 0;
24+
let sellIdx = 1;
25+
let profit = 0;
26+
27+
while (sellIdx < prices.length) {
28+
let buyPrice = prices[buyIdx];
29+
let sellPrice = prices[sellIdx];
30+
31+
// 더 낮은 가격에 매수 가능한 날을 찾으면 바로 거기서부터 재탐색
32+
if (buyPrice > sellPrice) {
33+
buyIdx = sellIdx;
34+
} else {
35+
let newProfit = sellPrice - buyPrice;
36+
profit = Math.max(profit, newProfit);
37+
}
38+
39+
sellIdx++;
40+
}
41+
42+
return profit;
43+
};

best-time-to-buy-and-sell-stock/liza0525.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,25 @@ def maxProfit(self, prices: List[int]) -> int:
2323

2424
# 최종적으로 기록된 max_profit이 답
2525
return max_profit
26+
27+
28+
# 7기 풀이
29+
# 시간 복잡도: O(n)
30+
# - prices의 길이 n만큼 순회
31+
# 공간 복잡도: O(1)
32+
# - 상수만 사용
33+
class Solution:
34+
# 첫날부터 매일의 가격이 최소인지, 최대인지를 판단하여 가장 큰 이익을 낼 수 있는 값을 계산한다.
35+
# 첫날부터 시간 순서대로 확인해야 함(기간 중 가장 최댓값이 가장 최솟값보다 빠를 수도 있기 때문)
36+
def maxProfit(self, prices: List[int]) -> int:
37+
min_sell_price = 10 ** 4 # 문제 조건에서 10의 4승이 최대라고 하여 이를 min_sell_price의 초기값으로
38+
max_profit = 0
39+
40+
for price in prices:
41+
# 매일 순회하며 지나간 시간들 중에 가장 작은 price인지 확인하여 업데이트
42+
min_sell_price = min(min_sell_price, price)
43+
44+
# 매일 순회하며 오늘 얻을 수 있는 profit과 이전에 얻었던 max_profit을 비교하여 업데이트
45+
max_profit = max(max_profit, price - min_sell_price)
46+
47+
return max_profit
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# 7기 풀이
2+
# 시간 복잡도: O(n)
3+
# - 인코딩 디코딩 모두 문자열 리스트의 길이에 따라 시간 복잡도가 결정됨
4+
# 공간 복잡도: O(1)
5+
# - 정답 변수인 res를 제외하고는 변수만 쓰임
6+
7+
8+
DELIMITER = "^"
9+
10+
11+
class Solution:
12+
def encode(self, strs):
13+
# DELIMITER를 사이에 두고 단어의 글자 수와 단어를 concat해서 저장한다.
14+
# 예) ["cat", "is", "cute"] -> "3^cat2^is4^cute"
15+
# 이렇게 해야 글자 수를 예측하고 split하기 쉬워진다.
16+
return "".join(map(lambda x: f"{len(x)}{DELIMITER}{x}", strs))
17+
18+
def decode(self, s):
19+
res = []
20+
i = 0
21+
while i < len(s):
22+
# i번째 글자로부터 첫번째 DELIMITER가 나오는 인덱스를 j라고 칭함
23+
j = s.index(DELIMITER, i)
24+
word_len = int(s[i:j]) # DELIMITER 앞의 글자는 '글자 수'이므로 int로 변환
25+
word = s[j + 1:j + word_len + 1] # i번째부터 글자 수 만큼 잘라서 word를 찾음
26+
res.append(word)
27+
i = j + word_len + 1 # 자른 이후의 index를 새로운 i로 설정하여 다음 글자 찾음
28+
return res

group-anagrams/Cyjin-jani.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const isAnagram = function (s, t) {
2+
if (s.length !== t.length) return false;
3+
4+
const data = new Map();
5+
6+
for (let char of s) {
7+
data.set(char, (data.get(char) || 0) + 1);
8+
}
9+
10+
for (let char of t) {
11+
if (!data.get(char)) return false;
12+
data.set(char, data.get(char) - 1);
13+
}
14+
15+
return true;
16+
};
17+
18+
// Time Limit Exceeded로 fail..
19+
// tc: O(n^2);
20+
// sc: O(n)
21+
const groupAnagrams_naive = function (strs) {
22+
if (strs.length < 2) return [strs];
23+
24+
const answer = [];
25+
26+
while (strs.length > 0) {
27+
let temp = [strs[0]];
28+
29+
if (strs.length < 2) return answer;
30+
31+
strs.splice(0, 1);
32+
33+
for (let j = 0; j < strs.length; j++) {
34+
if (isAnagram(temp[0], strs[j])) {
35+
// 같다면, temp 배열에 넣음
36+
temp.push(strs[j]);
37+
}
38+
}
39+
answer.push(temp);
40+
41+
temp.forEach((t) => {
42+
const idx = strs.indexOf(t);
43+
if (idx !== -1) {
44+
strs.splice(idx, 1);
45+
}
46+
});
47+
48+
if (strs.length === 1) {
49+
answer.push(strs);
50+
}
51+
}
52+
53+
return answer;
54+
};
55+
56+
// splice 같은 로직이 없어서 겨우 TLE을 통과했지만 여전히 O(n²)인 점은 변함이 없음.
57+
const groupAnagrams_set = function (strs) {
58+
const visited = new Set();
59+
const answer = [];
60+
61+
for (let i = 0; i < strs.length; i++) {
62+
if (visited.has(i)) continue;
63+
64+
const group = [strs[i]];
65+
66+
for (let j = i + 1; j < strs.length; j++) {
67+
if (!visited.has(j) && isAnagram(strs[i], strs[j])) {
68+
group.push(strs[j]);
69+
visited.add(j);
70+
}
71+
}
72+
73+
answer.push(group);
74+
}
75+
76+
return answer;
77+
};
78+
79+
//! AI로부터 힌트를 얻어 풀어봤습니다..
80+
// tc: O(nlogn)
81+
// sc: O(n)
82+
const groupAnagrams = function (strs) {
83+
const map = new Map();
84+
85+
for (const str of strs) {
86+
const key = str.split('').sort().join('');
87+
88+
if (!map.has(key)) {
89+
map.set(key, []);
90+
}
91+
map.get(key).push(str);
92+
}
93+
94+
return [...map.values()];
95+
};

group-anagrams/liza0525.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 7기 풀이
2+
# 시간 복잡도: O(n)
3+
# - 입력 받은 strs 리스트의 길이 만큼 순회하므로
4+
# 공간 복잡도: O(1)
5+
# - 결과로 반환할 result를 제외하고는 변수만 사용
6+
from collections import defaultdict
7+
8+
9+
class Solution:
10+
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
11+
result = defaultdict(list)
12+
13+
for str_ in strs:
14+
# 아나그램의 key를 동일하게 만든다. 갖고 있는 문자열의 오름차순 결과를 key로 만듦
15+
anagram_key = "".join(sorted(list(str_)))
16+
# 같은 key를 가진 것끼리 리스트로 묶어둔다.
17+
result[anagram_key].append(str_)
18+
19+
return list(result.values())
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 7기 풀이
2+
# Trie 전체 공간 복잡도: 공간: O(n * m)
3+
# - n은 단어 수, m은 평균 단어 길이
4+
# 각 메서드 마다 시간 복잡도와 공간 복잡도 작성
5+
class Node:
6+
def __init__(self, val=None):
7+
self.children = {} # 자식 노드를 담을 dictionary
8+
self.is_end = False # 현재 노드를 끝으로 하는 단어가 저장되었는지에 대한 flag
9+
10+
11+
class Trie:
12+
def __init__(self):
13+
self.root = Node()
14+
15+
def insert(self, word: str) -> None:
16+
# 시간: O(m) — m은 word의 길이, 문자 하나씩 순회
17+
# 공간: O(m) — 최악의 경우 m개의 새 노드 생성 (아무것도 공유 안할 때)
18+
node = self.root
19+
for s in word:
20+
if s not in node.children: # 아직 저장이 되지 않은 문자라면 children에 노드로써 저장해둔다.
21+
node.children[s] = Node(s)
22+
node = node.children[s] # 다음 노드를 children node로 지정하여 후손까지 저장할 수 있도록 함
23+
node.is_end = True # 모든 노드를 저장한 후에는 단어의 끝을 알리도록 is_end를 True로 변경
24+
25+
def search(self, word: str) -> bool:
26+
# 시간: O(m) — m은 word/prefix의 길이만큼 순회
27+
# 공간: O(1) — 새 노드 생성 없이 포인터만 이동
28+
node = self.root
29+
for s in word:
30+
if s not in node.children: # 찾으려는 단어의 문자가 저장되어 있지 않으면 search 실패이므로 False 반환
31+
return False
32+
node = node.children[s]
33+
34+
return node.is_end # 끝까지 돌아보고 해당 단어의 끝이 저장되어 있는지 아닌지를 반환하므로써 search의 성패 여부를 확인할 수 있음
35+
36+
def startsWith(self, prefix: str) -> bool:
37+
# start with의 경우에는, search와 거의 로직이 동일하나, is_end의 여부를 볼 필요가 없으므로 모든 문자에 대한 search가 끝나면 무조건 True 반환
38+
node = self.root
39+
for s in prefix:
40+
if s not in node.children:
41+
return False
42+
node = node.children[s]
43+
44+
return True
45+

word-break/liza0525.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 7기 풀이
2+
# 시간 복잡도: 시간복잡도: O(n * m)
3+
# - n은 s의 길이, m은 wordDict의 평균 단어 길이
4+
# - 각 인덱스마다 wordDict를 순회하므로
5+
# 공간 복잡도: 공간복잡도: O(n)
6+
# - memo에 최대 n개의 인덱스를 저장
7+
class Solution:
8+
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
9+
memo = set()
10+
11+
def dfs(i):
12+
if i == len(s):
13+
# i가 s의 길이가 되면 모든 탐색이 성공으로 끝났기 때문에
14+
# True로 반환
15+
return True
16+
17+
memo.add(i) # 해당 인덱스에 대해서는 방문을 했기 때문에 memo
18+
for word in wordDict:
19+
if i + len(word) in memo:
20+
# i + len(word)가 memoization해둔 인덱스라면 이미 성공한 케이스므로 통과
21+
continue
22+
23+
if i + len(word) > len(s):
24+
# i + len(word)가 len(s)보다 크면 탐색을 더이상 할 수 없으므로 continue
25+
continue
26+
27+
# 아직 방문을 하지 않은 index라면 단어를 slice하여 실제로 wordDict에 있는지 확인
28+
if s[i:i + len(word)] in wordDict:
29+
# 다음 인덱스 i + len(word)로 dfs 탐색
30+
check = dfs(i + len(word))
31+
if check:
32+
# 모든게 성공되었을 땐 True 반환
33+
return True
34+
35+
return False
36+
37+
return dfs(0)

0 commit comments

Comments
 (0)