프로그래머스
작성 수정
프로그래머스 고득점 Kit 의 해시, 완전탐색, 정렬 문제와 Level 1 문제, 모의 테스트 두 문제다. 2021년 11월에 Python 으로 풀고, 2022년 3월에 Level 1 을 더 풀었다. 가장 큰 수와 타겟 넘버는 JavaScript 로도 다시 썼다. 소수 찾기의 첫 풀이는 소수 판별 조건이 틀려 있어서 해설을 보고 다시 썼고, 그 다시 쓴 쪽도 check(int(i)) 에 순열이 아니라 길이를 넘기는 실수가 남아 있다.
해시: 완주하지 못한 선수
# 1. collections Counter객체 풀이
# 1) 원소:빈도의 딕셔너리형태, multiset이라고도 부름
# 2) Counter객체로 뺄셈이 가능하다
import collections as col
def solution(p, c):
answer = col.Counter(p) - col.Counter(c)
return list(answer.keys())[0]
# 2. zip 풀이
# 1) 인데스별로 묶이며, 가장 짧은 리스트를 기준으로 맞춰진다.
def solution(p,c):
p.sort()
c.sort()
for p,c in zip(p,c):
if p!=c:
return p
return p[-1]
# 3. hash 풀이
def solution(p,c):
temp = 0
dic={}
for p in p:
dic[hash(p)] = p
temp += int(hash(p))
for c in c:
temp -= hash(c)
answer =dic[temp]
return answer
해시: 전화번호 목록
# 1. 문제를 100% 이해하지 못했다.
# 2. 출제자의 의도, 풀이의 핵심을 이해해보도록 하자
'''
'접두어'에 FOCUS!
'''
# 1. startswith() 풀이 방법
def solution(phoneBook):
phoneBook = sorted(phoneBook)
# 새로 안 사실
# 접두어별로 있는지 확인 = 문자열 기준 앞의 인덱스 부터 확인
for p1, p2 in zip(phoneBook,phoneBook[1:]):
if p2.startswith(p1):
return False
return True
# 2. hash 풀이 방법
def solution(phoneBook):
dic={}
for phone in phoneBook:
dic[phone] = 1
for phone in phoneBook:
temp =""
for number in phone:
temp +=number
# 각 자리를 붙은 게 dic안에 있으면 and 자기 자신은 제외(어차피 중복 안되니까)
if temp in dic and temp != phone:
return False
return True
해시: 위장
# 곱집합 연산
# (A+1) x (B+1) - 1 로 계산했다. 1 을 빼주는 이유는
# (A+1) x (B+1) 의 경우에는 비어있는 경우도 포함되기 때문이다.
def solution(clothes):
dic={}
for item in clothes:
if item[1] not in dic:
dic[item[1]] = 1
else:
dic[item[1]] +=1
cases = 1
for item in dic.values():
cases *= item+1
return cases-1
해시: 베스트앨범
def solution(genres, plays):
answer = []
dic = {}
li = []
# genres와 plays 합치기
for music in zip(genres,plays):
li.append(music)
# 딕셔너리에 넣기
for idx,music in enumerate(li):
if music[0] not in dic:
dic[music[0]] =[]
dic[music[0]].append([idx,music[1]])
print(dic)
# 장르내에서 정렬하기
new_dic={}
for genre in dic.items():
music_list = sorted(genre[1],key=lambda x:x[1],reverse=True)
new_dic[genre[0]] = music_list
print(new_dic)
print("-------------------")
# 장르별 플레이 수 합치기
plays_sum={}
for genre, plays in dic.items():
sum_play = 0
for play in plays:
sum_play += play[1]
plays_sum[genre] = sum_play
# 많이 플레이 된 장르문 정렬
plays_list = sorted(plays_sum.items(),key=lambda x:x[1],reverse=True)
# 많이 플레이 된 장르문에서 노래 찾기
for genre in plays_list:
print(new_dic)
for i in range(2):
answer.append(new_dic[genre[0]][i][0])
# 만약 중복이 나오면 sort후 낮은 번호대로, idx리턴하기
return answer
def solution(genres, plays):
answer = []
dic1 = {}
dic2 = {}
for i, (g, p) in enumerate(zip(genres, plays)):
if g not in dic1:
dic1[g] = [(i, p)]
else:
dic1[g].append((i, p))
if g not in dic2:
dic2[g] = p
else:
dic2[g] += p
for (k, v) in sorted(dic2.items(), key=lambda x:x[1], reverse=True):
for (i, p) in sorted(dic1[k], key=lambda x:x[1], reverse=True)[:2]:
answer.append(i)
return answer
완전탐색: 모의고사
처음 풀이
def solution(answers):
# 문제 개수
num = len(answers)
# 찍는 패턴
# 문제 수 만큼 리스트 추가하기
no1 = [1,2,3,4,5] * (num // 5 + 5)
no2 = [2,1,2,3,2,4,2,5] * (num // 8 + 8)
no3 = [3,3,1,1,2,2,4,4,5,5] * (num // 10 + 10)
count1 = 0
count2 = 0
count3 = 0
# answers순서대로 답이 맞는지 카운트 하기
for i in range(num):
if answers[i] == no1[i]:
count1 +=1
if answers[i] == no2[i]:
count2 +=1
if answers[i] == no3[i]:
count3 +=1
# max 카운트 출력하기
max_val = max(count1,count2,count3)
# 가장 큰 수의 수포자 찾기
answer = []
if count1 == max_val:
answer.append(1)
if count2 == max_val:
answer.append(2)
if count3 == max_val:
answer.append(3)
return answer
다시 풀기. 인덱스를 패턴 길이로 나눈 나머지로 돈다
def solution(answers):
answer = []
# 패턴 입력
no1 = [1,2,3,4,5]
no2 = [2,1,2,3,2,4,2,5]
no3 = [3,3,1,1,2,2,4,4,5,5]
count = [0,0,0]
# 인덱스가 없어서 list index out of range뜸
# i%배열의크기를 이용한 인덱스 주기 활용
for idx,val in enumerate(answers):
if answers[idx] == no1[idx%len(no1)]:
count[0] +=1
if answers[idx] == no2[idx%len(no2)]:
count[1] +=1
if answers[idx] == no3[idx%len(no3)]:
count[2] +=1
# max값을 활용하여 answer에 답 추가
max_val = max(count)
answer =[]
for i in range(1,4):
if count[i-1] == max_val:
answer.append(i)
return answer
완전탐색: 소수 찾기
처음 풀이
from itertools import permutations
import math
def solution(numbers):
# numbers로 받은 숫자 리스트에 각각 저장하기
li = list(numbers)
num = len(li)
poss =[]
answer = 0
# 조합 가능한 숫자 리스트에 저장하기
for i in range(1,num+1):
d = list(permutations(li,i))
for j in range(len(d)):
if d[j][0] != '0':
poss.append(''.join(d[j]))
# 중복되는 숫자 없애기
set_poss = set(poss)
# 소수 찾기
for item in set_poss:
count = 0
for dividor in range(2, int(math.sqrt(int(item)))+1):
# 소수가 아니라면 반복문 바로 끝내기
if int(item) ==1 and int(item) % dividor == 0:
count =1
break
if count ==0:
answer +=1
# 시간탐색: 문자열 길이가 최대 7이라면, 가능한 예비 문자열은
# 1) 7 + 7*6 + 7*6*5 + 7*6*5*4 + ... + 7*6*5*4*3*2*1
# 7 + 42 + 200 + 800 + 2400 + 5040 -> 대략 1만 이하
# 2) 소수확인하는 방법: 2부터 그숫자-1까지 %값이 0이면 break
# 9,999,999 소수 확인 하려면 엄청난데?
# 100부터 51이상은 보지 않아도 되는 것 처럼, 쓸데없는 연산은 빼면 됌
# /2 말고 좀 더 획기적으로 줄 일 수 있는 방법 없나?
# 제곱근 범위까지 볼까, 천만이면, 제곰근이 3천대된다.
return answer
print(solution("17"))
해설 참고
from itertools import permutations
import math
def check(n):
# 0과 1은 무시
if n < 2:
return False
for i in range(2,int(math.sqrt(n))+1):
if n % i == 0:
return False
return True
def solution(numbers):
answer = []
for i in range(1,len(numbers)+1): #len(문자열) 해도 체크가 된다.
# 조합 가능한 숫자 리스트에 저장하기
d = list(map(''.join, permutations(list(numbers),i))) # 숫자 합치기
for j in list(set(d)) : # 중복 제거한 숫자
if check(int(i)): # 소수인지 확인
answer.append(int(i))
answer =len(set(answer)) # 7, 7인 경우 77,77은 두 번 나오기 때문에
return answer
print(solution("17"))
완전탐색: 카펫
def solution(brown, yellow):
# w>= h
# brown = 2w + 2h - 4
# yellow = w*h-brown
# w*h = brown+yellow
# w+h = brown/2 + 2
for w in range(3,brown//2-2+1):
for h in range(3,w+1):
if w*h == brown+yellow and w+h ==brown//2 + 2 :
return w,h
return []
정렬: K번째수
def solution(array, commands):
answer = []
for i in range(len(commands)):
li = sorted(array[commands[i][0]-1:commands[i][1]])
answer.append(li[commands[i][2]-1])
return answer
'''
새로 알게 된 사실
1. list[i][i] = []
2. 문제에서 흔히 I번째에서 J번째까지라고 함은 list[i-1][j]이다
3. k번째 수는 인덱스로 k-1이다
'''
정렬: 가장 큰 수
Python
def solution(numbers):
numbers = list(map(str,numbers))
numbers.sort(key=lambda x:x*3, reverse=True)
return str(int(''.join(numbers)))
'''
from itertools import permutations
def solution(numbers):
li = sorted(map(''.join,list(permutations(list(map(str,numbers)),len(numbers)))),reverse=True)
answer = li[0]
return answer
'''
# 시간 복잡도를 생각 못했다.
# 배열의 개수가 100000까지 되니깐, O(100000!)은 당연히 TImeOut이다
'''
변환된 num을 sort()를 사용하여 key 조건에 맞게 정렬한다.
lambda x : x*3은 num 인자 각각의 문자열을 3번 반복한다는 뜻이다.
x*3을 하는 이유? -> num의 인수값이 1000 이하이므로 3자리수로 맞춘 뒤, 비교하겠다는 뜻.
예시가 잘못되서 햇갈렸다.
6,2,10을 비교하는 게 아니라
121 12를 비교할 때
[121,12] 121 21 > 12 112 이므로
121: 121121
12: 121212 12가 더 큰 수 이는 1000이하의 숫자인 것을 감안해봤을 때 단순히 문자열을 반복해줌으로써 값 비교 가능해짐
'''
JavaScript
function solution(numbers) {
const result = numbers
.map((number) => String(number))
.sort((a, b) => b + a - (a + b)) // 💛
.join('');
return result[0] === '0' ? '0' : result;
}
console.log(solution([6, 2, 10]));
// sort((a,b) => (b+a)-(a+b)) = 문자로 변환된 숫자를 연결하여 비교정렬
// ( '3', '30' => ('303')-('330'))
정렬: H-Index
def solution(citations):
citations.sort(reverse=True)
for idx , citation in enumerate(citations):
if idx >= citation:
return idx
return len(citations)
# h번 이상 인용된 논문이 h개 이상, 나머지 논문이 h개 이하
# 가장 많이 인용된 = h가 최대인 값 부터 차례대로 검색
# idx = 논문의 수, citation은 논문인용횟수
# 논문의 수가 논문인용횟수보다 크거나 같을 경우, 즉 문제에서 나머지 논문이 h개 이하일 때는 고려하지 않아도 된다.
DFS/BFS: 타겟 넘버
Python
answer = 0
def DFS(numbers, target, result):
global answer
if numbers ==[]:
if target == result:
answer +=1
return
DFS(numbers[1:], target, result+numbers[0])
DFS(numbers[1:], target, result-numbers[0])
def solution(numbers, target):
global answer
DFS(numbers, target, 0)
return answer
JavaScript
let answer = 0;
function DFS(arr, target, sum) {
if (arr.length === 0) {
// arr =[] ❌
if (target === sum) {
answer += 1;
}
return;
}
DFS(arr.slice(1), target, sum + arr[0]);
DFS(arr.slice(1), target, sum - arr[0]);
}
function solution(numbers, target) {
DFS(numbers, target, 0);
return answer;
}
console.log(solution([1, 1, 1, 1, 1], 3));
Level 1: 문자열을 정수로 바꾸기
def solution1(s):
li = list(map(int, s))
if s[0] is '-':
return ''.join(li) * -1
else:
return ''.join(li)
def solution2(s):
return int(s[1:]) * -1 if s[0] == '-' else int(s)
def solution3(s):
return int(s)
# 형변환 시 부호는 알아서 바뀜
# 진수 변경 로직
def solution4(s):
result = 0
for idx, number in enumerate(str[::-1]):
if number == '-':
result *= -1
else:
result += int(number) * (10 ** idx) # 이부분
return result
Level 1: 신고 결과 받기
내 방식
def solution(id_list, report, k):
answer =[0 for _ in range(len(id_list))]
# 유저별로 신고한 유저들이 저장된다.
reported_name = {name:[] for name in id_list}
# 유저별로 몇번 신고된지 저장된다.
reported_count ={name:0 for name in id_list}
# set함수를 이용해 동일 신고 무시
for item in set(report):
user, reported_user = item.split(" ")[0], item.split(" ")[1]
reported_count[reported_user] += 1
reported_name[reported_user].append(user)
# key: 신고당한 유저, values: 신고한 유저
for key, values in reported_name.items():
if reported_count[key] >= k:
for value in values:
answer[id_list.index(value)] +=1
return answer
참고 풀이
def solution(id_list, report, k):
db = {name:[] for i, name in enumerate(id_list)}
reports = {name:0 for i, name in enumerate(id_list)}
for re in set(report):
user = re.split(" ")[0]
reported_user = re.split(" ")[1]
db[user].append(reported_user)
reports[reported_user] +=1
answer = [0 for _ in range(len(id_list))]
for key, values in db.items():
for value in values:
if reports[value] >=k:
answer[id_list.index(key)] += 1
return answer
Level 1: 나머지가 1이 되는 수 찾기
def solution(n):
x = 1
while(x):
if(n%x==1):
break
x+=1
return x
# 참고, 나머지가 1인 수는 약수-1 % ==0랑 같음
def solution(n):
for i in range(2,n//2+1):
if n % i == 1:
return i
return n-1
# for문 도중에 return을 하면 반복문 탈출이 아니라 그냥 리턴이 되네
# 범위: n끝까지 볼 게 아니라, 답이 반드시 있다고 했으니까 2로 나눴을 때까지만 보고 아니면 n-1하면 되겠네
모의 테스트: 나머지 한 점
def solution(v):
x = [i[0] for i in v]
y = [i[1] for i in v]
for i in range(2):
x[0] ^= x[i+1]
y[0] ^= y[i+1]
return [x[0],y[0]]
# ^ XOR연산은 다르면 1이다.
# 따라서 x[0]과 x[1], x[2]을 비교해서 x[0]과 다르면 x[0]에 대입하는 형식으로, 다른 좌표를 찾아낸다.
# ^ 비트연산을 통해 같은 값이 두개있으면 나머지 한 값이 나오네
## counter.collection을 이용한 방법
from collections import Counter
def solution(v):
x = [i[0] for i in v]
y = [i[1] for i in v]
x_count = Counter(x)
y_count = Counter(y)
x_answer = [i for i in x_count if x_count[i] == 1]
y_answer = [i for i in y_count if y_count[i] == 1]
answer = x_answer+y_answer
return answer
모의 테스트: 순열 확인
def solution(arr):
arr.sort()
for index in range(len(arr)):
if arr[index] != (index+1) : return False
return True
# 문제의 유니버스를 잘 살폈어야 했다.
# 이 문제는 무조건 소팅하면 1부터 끝까지 가는 유니버스안에 있었다. 이를 캐치했어야 했다.
# 짜잘하게 for문 문법과, 인덱스 문법, sort문법을 복습하는 계기가 됐다.