오답 😔
def reorderLogFiles(self, logs: List[str]) -> List[str]: return sorted(logs, key=lambda x: (x.split()[1], x.split()[0]))
- [Wrong Answer]
Input: ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"]
Output: ["dig2 3 6","dig1 8 1 5 1","let1 art can","let3 art zero","let2 own kit dig"]
Expected: ["let1 art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1","dig2 3 6"]잘못된 점
1. 첫 번째 정렬 기준인x.split()[1]
로 인해 digit-log들이 앞에 나옴
2. digit-log들은 입력 순서를 유지해야 하는데 이것도 숫자 순서대로 정렬이 바뀜
👉 digit-logs와 letter-logs를 따로 정렬해야겠다오답 2 😔
def reorderLogFiles(self, logs: List[str]) -> List[str]: letters = filter(lambda x: x.split()[1].isalpha(), logs) letters = sorted(letters, key=lambda x: (x.split()[1], x.split()[0])) digits = list(filter(lambda x: x.split()[1].isdigit(), logs)) return letters + digits
- [Wrong Answer]
Input: ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo"]
Output: ["a8 act zoo","g1 act car","ab1 off key dog","a1 9 2 3 1","zo4 4 7"]
Expected: ["g1 act car","a8 act zoo","ab1 off key dog","a1 9 2 3 1","zo4 4 7"]letter-log들은 로그명 문자열 끝까지 비교하고 같으면 identifier를 기준으로 정렬해야 하는데
x.split()[1]
때문에 첫 단어만 일치하면 그 다음에 바로identifier로 정렬함.
정답 🥳
def reorderLogFiles(self, logs: List[str]) -> List[str]: letters = filter(lambda x: x.split()[1].isalpha(), logs) letters = sorted(letters, key=lambda x: (" ".join(x.split()[1:]), x.split()[0])) digits = list(filter(lambda x: x.split()[1].isdigit(), logs)) return letters + digits👉 'python list to string'으로 구글링 결과... 잊고 있었던
join
의 존재를 깨달음^^!
다른 solution 😮
def reorderLogFiles(self, logs: List[str]) -> List[str]: letters, digits = [], [] for i in logs: if i.split()[1].isdigit(): digits.append(i) else: letters.append(i) letters = sorted(letters, key=lambda x: (x.split()[1:], x.split()[0])) return letters + digits👉 letter_log, digit_log 구분을 위해 filter 두 번 쓰는 것보다 그냥 for문 + if-else문으로 처리하는 게 더 빠름!
👉x.split()[1:]
을 string으로 만들 필요 없이.... 그냥 그대로 key로 써도 되는 거였다ㅎㅎ;;
배운 것 💪✨
- sorted(list, key=(1, 2, 3...): key(정렬기준) 여러 개 설정 가능! filter, map 함수와 달리 list()를 쓸 필요없이 바로 리스트를 반환해 줌
- list = sorted(list, key=...)는 list.sort(key=...)와 같음
- " ".join(list): 리스트를 문자열로 바꿀 때 사용
오답 😔
def mostCommonWord(self, paragraph: str, banned: List[str]) -> str: paragraph = list(filter(lambda x: x not in banned, paragraph.lower().split())) return max(paragraph, key=lambda x: paragraph.count(x))
- [Wrong Answer]
Input: "Bob hit a ball, the hit BALL flew far after it was hit."
["hit"]
Output: "bob"
Expected: "ball"터미널에서 필터함수를 적용한 paragraph를 확인해 본 결과 "ball," 과 "ball"이 따로 카운트되어서 모든 단어의 카운트수가 1이 되었다. 그래서 맨 앞에 위치한 bob이 리턴된 것...
👉 맨 첫줄에re.sub("[^a-z ]", " ", paragraph.lower())
을 추가했는데 아무 변화도 일어나지 않음
why? 이 코드는 치환된 값을 반환할 뿐 원래 문자열을 바꿔주진 않기 때문에 앞에paragraph =
을 붙여 줘야 함!
정답 🥳
1. count 이용 def mostCommonWord(self, paragraph: str, banned: List[str]) -> str: paragraph = re.sub("[^a-z ]", " ", paragraph.lower()) paragraph = list(filter(lambda x: x not in banned, paragraph.split())) return max(paragraph, key=lambda x: paragraph.count(x)) 2. collections 모듈의 Counter 이용 (더 빠름!) def mostCommonWord(self, paragraph: str, banned: List[str]) -> str: paragraph = re.sub("[^a-z ]", " ", paragraph.lower()) paragraph = list(filter(lambda x: x not in banned, paragraph.split())) dic = collections.Counter(paragraph) return max(dic.keys(), key=lambda x:dic[x])
다른 solution 😮
def mostCommonWord(self, paragraph, banned): words = [word for word in re.sub(r'[^\w]', ' ', paragraph).lower().split() if word not in banned] counts = collections.Counter(words) return counts.most_common(1)[0][0]👉 Counter 객체에는 most_common()이라는 메서드가 있었다~~!
배운 것 💪✨
- 정규표현식의 sub 메서드는 새 문자열을 반환할 뿐, 기존 문자열을 변하지는 않음 👉 str = re.sub('', '', str)의 형태로 작성해야 함
- collection.Counter(string/list)
value: 개수
로 구성된 딕셔너리 반환
- Counter 객체의 most_common(n) 메서드
(value, 개수)
로 구성된 리스트 반환. 파라미터가 없으면 전체 튜플을, 있으면 개수가 큰 순서로 n개의 튜플로 구성된 리스트 반환.
정답 🥳
def groupAnagrams(self, strs: List[str]) -> List[List[str]]: #step1 li = list(map(lambda x: sorted(list(x)), strs)) #step2 li = sorted(enumerate(li), key = lambda x: x[1]) ans = [] num = 0 for i in range(len(li)): #step3 if (i==0): ans.append([strs[li[i][0]]]) elif li[i][1] == li[i-1][1]: ans[num].append(strs[li[i][0]]) else: ans.append([strs[li[i][0]]]) num += 1 return ans
- step1
각 단어를 리스트화하고, 알파벳을 재정렬한 리스트로 만들기
e.g. ["tan", "pat", "nat"] -> [['a', 'n', 't'], ['a', 'p', 't'], ['a', 'n', 't']]
- step2
해당 리스트들을 enumerate를 이용해 기존 인덱스와 리스트를 담은 튜플로 만들고, 리스트 컨텐츠대로 정렬하기
e.g. [(0, ['a', 'n', 't']), (2, ['a', 'n', 't']), (1, ['a', 'p', 't'])]
- step3
각 튜플(li[i])에 대해서
- 맨 첫 튜플이면: 해당 인덱스(li[i][0])를 갖는 단어(strs[li[i][0])를 ans에 리스트로 추가
- 이전 튜플과 value(li[i][1])가 같으면: 해당 인덱스를 갖는 단어를 ans의 가장 최근 리스트(ans[num]) 요소로 추가
- 이전 튜플과 value가 다르면: 해당 인덱스를 갖는 단어를 ans에 새 리스트로 추가, 가장 최근 리스트의 인덱스(num)에 +1
수정에 수정을 거쳐 완성한 코드와... 글로 풀어써도 장황한 설명...^.ㅠ
👉 각 단어(스트링)를 단어내에서 알파벳순으로 재정렬하려고 리스트로 만든 후 sort를 썼는데... 그냥 스트링에 sort를 바로 써도 됨(but 리턴값은 리스트)
다른 solution 😮
def groupAnagrams(self, strs: List[str]) -> List[List[str]]: anagrams = collections.defaultdict(list) for i in strs: anagrams["".join(sorted(i))].append(i) return list(anagrams.values())👉 단어들을 재정렬한 후 defaultdict에
key(재정렬한 공통단어): value(기존 단어 리스트)
로 추가하고 values만 출력하여 훨씬 간단하게 푼 방법!
👉 키값을 그냥 sorted(i)가 아닌 "".join(sorted(i))로 설정한 이유: 리스트는 가변객체이기 때문에 딕셔너리의 키로 설정할 수 없음! (TypeError 발생)
배운 것 💪✨
- sorted("apple") = ['a', 'e', 'l', 'p', 'p']
"".join(sorted("apple")) = "aelpp"
collections.defaultdict()
특정 key의 value가 존재하지 않는 경우, 인자로 주어진 객체의 기본값(int: 0 / str: "" / list: [])을 초기값으로 지정하는 유사딕셔너리dessert = collections.defaultdic(list) dessert['bread'] dessert['icecream'] dessert['coffee'] print(dessert) #{'bread':[], 'icecream':[], 'coffee':[]}
- 리스트는 딕셔너리의 키가 될 수 없음
오답 😔
def longestPalindrome(self, s: str) -> str: ans = "" for i in range(len(s)): for l in range(i+1, len(s)): if s[i] == s[l]: sub = s[i:l+1] if sub == sub[::-1] and len(sub) > len(ans): ans = sub return ans
- [Wrong Answer] input="a"일 때 "a"가 아닌 ""가 출력
👉 ans의 초기값을 빈 문자열이 아닌 s[0]으로 설정
정답 🥳
def longestPalindrome(self, s: str) -> str: ans = s[0] for i in range(len(s)): for l in range(i+1, len(s)): if s[i] == s[l]: sub = s[i:l+1] if sub == sub[::-1] and len(sub) > len(ans): ans = sub return ans
다른 solution 😮
def longestPalindrome(self, s: str) -> str: #step1 def expand(left, right): while left >= 0 and right < len(s) and s[left] == s[right]: left -= 1 right +=1 return s[left+1:right] #step2 if len(s)<2 or s==s[::-1]: return s #step3 result = '' for i in range(len(s)): result = max(result, expand(i, i), expand(i, i+1), key=len) return result
- step1
left, right 포인터를 인자로 받아 s[left]와 s[right]가 동일할 경우 포인터를 앞뒤로 확장하며 가장 긴 펠린드롬을 리턴하는 expand 함수를 작성한다.- step2
빠른 예외 처리를 위해 expand 판별이 필요없는 경우부터 리턴- step3
for문과 expand 함수를 이용해 홀수(expand(i, i)), 짝수(expand(i, i+1)) 두 가지 경우를 고려, 팰린드롬 여부를 판별하면서 우측으로 이동한다. 이렇게 판별한 최댓값이 최종 결과가 된다.
배운 것 💪✨
- 투포인터 방식
- 리스트의 양쪽 끝에서부터 2개의 점 위치를 기록하며 순차적으로 자료를 처리하는 알고리즘
- 주로 정렬된 리스트에서 유용
- 특정 합을 가지는 부분 연속 수열 문제 등에 적용
- 예외 처리를 미리 하면 처리 속도가 훨씬 빨라짐!
정답 🥳
def arrayPairSum(self, nums: List[int]) -> int: nums.sort() res = 0 for i in range(0, len(nums), 2): res += nums[i] return res👉 sort를 이용해 오름차순으로 정렬해준 뒤 짝수번째 값(두 개씩 묶었을 때 더 작은 값이므로)의 합을 출력의 합을 출력
다른 solution 😮
def arrayPairSum(self, nums: List[int]) -> int: return sum(sorted(nums)[::2])
배운 것 💪✨
- list[::n]
처음부터 끝까지 n씩 인덱스가 증가하는 슬라이싱