728x90

코딩테스트 연습 - 올바른 괄호 | 프로그래머스 스쿨 (programmers.co.kr)

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 


def solution(s):
    stack = []
    for i in s:
        if i == '(':
            stack.append(i)
        else:
            if stack == []: 
                return False
            else:
                stack.pop()
    if stack != []: 
        return False
    return True

Stack을 이용하여서 “좌측 괄호” 일 경우 Stack에 넣어두고 “우측 괄호”가 나오면 Stack에 저장되어 있는 좌측 괄호 값을 빼는 형태로 짝을 찾아가는 형태로 구성

  1. 문자열을 문자 배열로 구성하여 반복문 순회
  2. 순회하면서 좌측 괄호인 경우는 Stack에 넣기
  3. Stack 값이 비었을 경우 false 리턴 ⇒ 짝이 맞지 않는 경우
  4. 순회하면서 우측 괄호인 경우 Stack에서 빼기
  5. 순회가 끝나고 Stack에 값이 존재하지 않으면 false 리턴

 

728x90
728x90

문제링크
https://school.programmers.co.kr/learn/courses/30/lessons/1844


문제 핵심

1. n * m 크기의 maps가 주어지고 maps에서 0은 벽을 1은 길을 나타낸다.

2. 목표지점은 maps의 우측 하단 끝 지점이며 시작지점은 (1,1) 이다.

3. 캐릭터는 상하좌우로 움직일 수 있으며, 벽으로 이동하는 것은 불가능하다.

4. 목표지점에 도달하기 위해 최소한의 움직임을 구하기


첫 번째 방법

최단거리를 구하는 문제이기에 BFS탐색법을 사용했지만 시간 초과가 발생하였다..ㅎㅎ

def solution(maps):
    w = len(maps) - 1 
    h = len(maps[0]) - 1 
    visit = [] 
    going = [[0,0,1]] 
    while going:
        now = going.pop(0) 
        x, y, count = now[0], now[1], now[2] 
        if x == w and y == h: 
            return count
        for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]: 
            nx = x + dx
            ny = y + dy
            if 0 <= nx <= w and 0 <= ny <= h and [nx, ny] not in visit and maps[nx][ny] == 1:
            
                going.append([nx, ny, count + 1]) 
                visit.append([nx, ny]) 
    return -1

기본적인 BFS 문제는 풀이방법이 비슷하다.
1. BFS문제면 이동할 때 사방팔방 다 이동하고 그 값을 저장하는 변수 필요 => 큐 사용

2. 위로 이동하고 좌우 옆으로 이동했을 때 해당 좌우가 방문했는지 저장하는 변수 필요  => 좌, 우, 상, 하 이동

 

1. 목표지점을 계산하기 위한 maps의 세로 길이 가로 길이를 w ,h 로 정의

2. visit라는 방문한 지점을 담을 리스트 정의

3. going은 방문해야할 지점을 담을 리스트

4. now 는 현재 지점이고 x, y, count 는 현재 x, y좌표 및 이동 거리

5. 만약 목표지점일 경우 이동 거리를 리턴

6. dx, dy 에는 (1, 0), (-1, 0), (0, 1), (0, -1) 상하좌우 이동의 값을 넣고

7. 경계 및 벽에 막히지 않고, 방문한 지점이 아닐 경우 방문할 지점에 추가하고 방문한 지점에 추가

8.  모든 경로를 탐색후에도 만족하지 않으면 -1 리턴

 


그래서 뭐가 문제인가.. 알아봤는데 나는 방문한 지점을 담은 리스트 visit를 리스트로 사용했고 이 경우에서 방문 여부를 확인하는 로직인 not in을 사용할 경우 시간복잡도가 크게 상승한다는 얘기.. 이를 해결하기 위해 visit를 set로 선언하여 통과하였다.


두 번째 방법

def solution(maps):
    w = len(maps) - 1
    h = len(maps[0]) - 1
    visit = set() 
    going = [[0,0,1]]
    while going:
        now = going.pop(0)
        x, y, count = now[0], now[1], now[2]
        if x == w and y == h:
            return count
        for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            nx = x + dx
            ny = y + dy
            if 0 <= nx <= w and 0 <= ny <= h and (nx, ny) not in visit and maps[nx][ny] == 1:
                going.append([nx, ny, count + 1])
                visit.add((nx,ny))
    return -1

set 자료형의 경우 내부 해시테이블을 이용하여 포함여부를 확인할 때 리스트보다 빠른 속도로 확인이 가능하여 시간을 줄여 해결할 수 있었다.


세 번째 방법

뭔가 내가  쓴건 깔끔하지 않다고 해야하나.. 그래서 다른 사람 코드를 봤더니

going 리스트도 deque를 사용하여 속도를 더욱 줄이는 방법이 있고 깔끔했다.

from collections import deque
def solution(maps):
    w = len(maps) - 1
    h = len(maps[0]) - 1
    visit = set()
    going = deque([[0,0,1]]) 
    while going:
        now = going.popleft()
        x, y, count = now[0], now[1], now[2]
        if x == w and y == h:
            return count
        for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
            nx = x + dx
            ny = y + dy
            if 0 <= nx <= w and 0 <= ny <= h and (nx, ny) not in visited and maps[nx][ny] == 1:
                going.append([nx, ny, count + 1])
                visit.add((nx,ny))
    return -1

deque를 사용하면 기존의 pop(), append()의 시간복잡도가 O(N)에서 popleft()를 통한 시간복잡도 O(1)로 줄어들기 때문에 n이 커질 경우 더욱 빠르게 문제해결이 가능해진다.

728x90
728x90


뭐지.. 왜 문제 복사가 안되는거지!!!??

 

암튼.. 이제부터 블로그 제대로 작성할려고 노력..!


문제 정리

 

일단 이 문제는 제한사항을 봐야한다. (코딩테스트의 핵심은 제한사항을 잘 봐야한다!)

 

  1. 중복이 없다. 
    - 이말은 lost와 reverse 내의 원소 값이 유일해야한다는 점!
    => lost = [1,1,2,3] reserve = [2,2,3,4] 가 될 수 없다.

  2. 여벌의 체육복이 있는 학생(reserve) 도 도난 당했을 수 있다.
    - lost 값에 reserve 값이 공통적으로 존재할 수 있다는 의미!
    => lost = [1,2,3] reserve = [3,4,5] 처럼..
    그래서 문제를 보면 여벌의 체육복은 1벌 밖에 없으니 3번 학생은 체육복을 빌려줄수가 없다

>> 즉, lost와 reserve에 같은 값이 있다면 그 값은 reserve에서 제외시켜줘야한다.

 

 

step1. reserve의 요소들 중 lost에 동일하게 존재하는 값들은 모두 제거해줘야 한다. 

reserve_set = set(reserve) - set(lost)
lost_set = set(lost) - set(reserve)

//set 차집합 계산법
a = [1,3,5,6]
b = [2,4,6,8]
c = set(a) - set(b) //6이 공통

print(c) 
[1,3,5]

그걸 set 자료형으로 구현했다. set은 집합 자료형이므로객체끼리 집합 연산이 가능하다. 그래서 - 연산자를 사용하여 각 집합별로 차집합을 수행할 수 있다. 

 

step2. 예시를 보자

n lost reserve return
5 [2,4] [3,5] 5

 

 

학생 1 학생 2 학생 3 학생 4 학생 5
  x o x o

 

3, 5 번 학생은 여벌의 체육복을 갖고 있다. 4번 학생은 3번, 5번 학생으로부터 체육복을 받을 수 있다.

 

           - 4번 학생이 3번 학생의 체육복을 받았다고 가정. => 체육 시간 참가 가능

           - 2번 학생은 3번 학생의 체육복을 받을 수 있음에도 3번이 4번한테 줬기에 못 받음. => 체육 시간 참가 불가능

           - 4번 학생은 굳이 3번 학생이 빌려주지 않아도 5번 학생의 체육복을 받을 수 있음.

 

결론 : 체육복은 양 옆 학생에게 빌려줄 수 있으므로 왼쪽 요소부터 탐색 해야 함.

 

step3. 문제 풀이

def solution(n, lost, reserve):
    reserve_set = set(reserve) - set(lost)
    lost_set = set(lost) - set(reserve)
    
    for student in reserve_set:
        if student - 1 in lost_set:
            lost_set.remove(student - 1)
        elif student + 1 in lost_set:
            lost_set.remove(student + 1)
            
    answer = n - len(lost_set)
    return answer

reserve의 student -1 요소는 reserve 학생의 왼쪽 학생부터 빌려주고

만약에 없다면 오른쪽 학생을 주는 식으로 작성하면 끝

728x90
728x90

문제 설명

명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호가로 길이세로 길이
1 60 50
2 30 70
3 60 30
4 80 40

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.


제한사항
  • sizes의 길이는 1 이상 10,000 이하입니다.
    • sizes의 원소는 [w, h] 형식입니다.
    • w는 명함의 가로 길이를 나타냅니다.
    • h는 명함의 세로 길이를 나타냅니다.
    • w와 h는 1 이상 1,000 이하인 자연수입니다.

입출력 예sizesresult
[[60, 50], [30, 70], [60, 30], [80, 40]] 4000
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] 120
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] 133

입출력 예 설명

입출력 예 #1
문제 예시와 같습니다.

입출력 예 #2
명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.

입출력 예 #3
명함들을 적절히 회전시켜 겹쳤을 때, 모든 명함을 포함하는 가장 작은 지갑의 크기는 133(=19 x 7)입니다.

 


풀이

def solution(sizes):
    w = [] # 가로 값을 담을 빈 리스트 생성
    h = [] # 세로 값
    for i in range(len(sizes)): # sizes는 2차원 리스트
        if sizes[i][0] >= sizes[i][1]: # 조건 : 가로>=세로
            w.append(sizes[i][0]) 
            h.append(sizes[i][1]) 
        else: # 명함번호2 해당
            h.append(sizes[i][0]) 
            w.append(sizes[i][1]) 
    return max(w) * max(h)

위와 같이 풀었는데 아래와 같이 푸는 방법도 있더라..

def solution(sizes):
    w = []
    h = []
    for i in range(len(sizes)):
    	sizes[i].sort()
        w = max(w, sizes[i][0])
	h = max(h, sizes[i][1])

    return w * h

각 w, h를 비교해서 둘 중 큰 값을 한 리스트에 넣고 나머지를 리스트로 만든다. 두 개의 리스트 중 가장 큰 값을 뽑아서 곱하면 된다.

  1. w, h 리스트를 만든다.
  2. for문을 돌면서 가로와 세로 2개의 모서리 w, h 중 큰 값은 가로 w리스트에, 작은 값은 세로 h리스트에 담는다.
  3. 가로/세로 중 가장 큰 값으로 만든 사각형의 넓이인 두 개의 리스트에서 가장 큰 값이 곱한 값이 답이다.
  4. w, h중 큰 값을 모아서 그 중 큰 값과, w, h중 작은 값을 모아서 그 중 큰 값을 곱하면 간단하게 끝난다.
728x90
728x90

1. 문제

2. 풀이과정

해당 문제는 단어가 1자리부터 최대 5자리까지만 존재하므로 모든 경우를 구한 뒤에 찾는 것으로 해결하였다.

모든 경우를 찾을 때는 중복순열을 활용하여 찾아주었다.

 

  1. 중복순열을 구해주는 product 함수를 사용하기 위해 product 모듈을 불러온다. from itertools import product
  2. 중복순열을 저장해 줄 리스트를 생성한다. result = list()
  3. 최소 1자리 단어부터 최대 5자리 단어까지 존재하므로 각 자리 개수만큼 반복하며 for i in range(1, 6)
  4. 모음 리스트에서 중복을 허용하여 해당 자리 단어를 리스트로 생성한다. li = list(product(["A", "E", "I", "O", "U"], repeat=i))
  5. 중복순열을 구한 뒤 해당 순열을 하나씩 불러오며 for j in li
  6. 각 순열을 하나의 단어로 만들어 결과에 한 단어로 저장한다. result.append(''.join(k for k in j))
  7. 전부 구한 중복순열의 결과를 사전순으로 정렬한다. result.sort()
  8. 정렬한 중복순열 리스트에서 해당 단어의 인덱스를 찾아 1을 더해준 값이 정답이다. 인덱스 값으로 찾았기 때문에 번호를 붙일 때는 1을 더해줘야 한다. answer = result.index(word) + 1
 

3. 소스코드

from itertools import product

def solution(word):
    answer = 0
    
    result = list()
    for i in range(1, 6):
        li = list(product(["A", "E", "I", "O", "U"], repeat=i))
        for j in li:
            result.append(''.join(k for k in j))
        
    result.sort()
    answer = result.index(word) + 1

    return answer
728x90
728x90

문제

우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩치가 각각 (x, y), (p, q)라고 할 때 x > p 그리고 y > q 이라면 우리는 A의 덩치가 B의 덩치보다 "더 크다"고 말한다. 예를 들어 어떤 A, B 두 사람의 덩치가 각각 (56, 177), (45, 165) 라고 한다면 A의 덩치가 B보다 큰 셈이 된다. 그런데 서로 다른 덩치끼리 크기를 정할 수 없는 경우도 있다. 예를 들어 두 사람 C와 D의 덩치가 각각 (45, 181), (55, 173)이라면 몸무게는 D가 C보다 더 무겁고, 키는 C가 더 크므로, "덩치"로만 볼 때 C와 D는 누구도 상대방보다 더 크다고 말할 수 없다.

N명의 집단에서 각 사람의 덩치 등수는 자신보다 더 "큰 덩치"의 사람의 수로 정해진다. 만일 자신보다 더 큰 덩치의 사람이 k명이라면 그 사람의 덩치 등수는 k+1이 된다. 이렇게 등수를 결정하면 같은 덩치 등수를 가진 사람은 여러 명도 가능하다. 아래는 5명으로 이루어진 집단에서 각 사람의 덩치와 그 등수가 표시된 표이다.

이름(몸무게, 키)덩치 등수
A (55, 185) 2
B (58, 183) 2
C (88, 186) 1
D (60, 175) 2
E (46, 155) 5

위 표에서 C보다 더 큰 덩치의 사람이 없으므로 C는 1등이 된다. 그리고 A, B, D 각각의 덩치보다 큰 사람은 C뿐이므로 이들은 모두 2등이 된다. 그리고 E보다 큰 덩치는 A, B, C, D 이렇게 4명이므로 E의 덩치는 5등이 된다. 위 경우에 3등과 4등은 존재하지 않는다. 여러분은 학생 N명의 몸무게와 키가 담긴 입력을 읽어서 각 사람의 덩치 등수를 계산하여 출력해야 한다.

입력

첫 줄에는 전체 사람의 수 N이 주어진다. 그리고 이어지는 N개의 줄에는 각 사람의 몸무게와 키를 나타내는 양의 정수 x와 y가 하나의 공백을 두고 각각 나타난다.

출력

여러분은 입력에 나열된 사람의 덩치 등수를 구해서 그 순서대로 첫 줄에 출력해야 한다. 단, 각 덩치 등수는 공백문자로 분리되어야 한다.

제한

  • 2 ≤ N ≤ 50
  • 10 ≤ x, y ≤ 200

예제 입력 1 복사

5
55 185
58 183
88 186
60 175
46 155

예제 출력 1 복사

2 2 1 2 5

풀이

 

N = int(input()) # 전체 사람 수
people = [] # 사람 정보를 받을 빈 list 생성

for _ in range(N): 
    x, y = map(int, input().split()) # 키와 몸무게 입력
    people.append((x,y)) # 그 값을 people 리스트에 추가

for i in people:
    rank = 1 # 초기값은 전부 1
    for j in people:
        if i[0] < j[0] and i[1] < j[1]: # 키와 몸무게 둘 다 커야한다는 and 조건 
            rank += 1 # 위 조건 만족 시 count + 1
    print(rank, end = " ")

문제 속에 답이 다 나와있는 문제였다.

일단, 자신보다 몸무게와 키 모두 큰 사람의 수 + 1이 자신의 덩치 등수가 되는 것을 알 수 있다.

 

=> ( 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩치가 각각 (x, y), (p, q)라고 할 때 x > p 그리고 y > q 이라면 우리는 A의 덩치가 B의 덩치보다 "더 크다"고 말한다.

 

따라서 입력받은 몸무계와 키를 각각의 사람마다 묶어서 people 리스트에 저장해준 후,

자신보다 몸무게 그리고 키가 모두 큰 사람의 수를 세는 것으로 쉽게 해결할 수 있다. 

728x90
728x90

문제

셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.

양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 

예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.

33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 

생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

입력

입력은 없다.

출력

10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

예제 입력 1 복사

예제 출력 1 복사

1
3
5
7
9
20
31
42
53
64
 |
 |       <-- a lot more numbers
 |
9903
9914
9925
9927
9938
9949
9960
9971
9982
9993

 

 


흠... 일단 문제가 너무 이해가 안갔다... 그래서 끄적끄적 코드를 쳐보니 

natural = set(range(1, 10001)) 
generated = set() 

for n in range(1, 10001):
    for j in str(n):
        n += int(j) 
        
    generated.add(n) 
self_num = sorted(natural - generated) 
for i in self_num:
    print(i)

 

1. 자연수 1~ 10000을 natural set에 넣어준다. 이 친구는 생성자가 없는 숫자를 골라내기 위한 용도로 쓰인다.

2. generated set는 d(n)들을 넣어줄 set이다. 빈방 상태로 시작한다.

3. 첫 번째 for문을 돌린다. 1부터 10000까지 돌리는 이유는, 1부터 10000 사이에서 나올 수 있는 생성자를 빠짐없이 찾기 위해서다. 문제에 있는 것처럼 d(d(d(... n)))수열식으로 꼬리 물면서 찾으면, 시작점에 따라 수열이 달라지는 셀프 넘버의 특성상 코드가 복잡해진다.

 

어.. 그래... 1부터 10000까지 계산한다고 하자.... generated set에 들어가는 숫자가 중복될 수 있으면,,, 101 같은 경우는 생성자가 2갠데 어떡해? -> 이는 중복을 허용하지 않는 set으로 해결

 

4.  그럼 이제 두 번째 for문을 보자. n를 str(n)으로 바꿨다!! -> indexing 해서 자릿수 씹고 뜯고 맛보려고...!

그리고 indexing 되어 한 글자씩 뽑힌 j를 int(j)로 형 변환해준다. -> 사칙연산하려고..!... n += int(j) 하려고...!

이렇게 각 자릿수를 원래 자기 수 n에 더해주면서 d(n)이 생성!

5. 이 d(n)은 set에 인자를 넣어주는 add()를 사용해서 generated에 집어넣어 준다.

6. 그럼 이제 1부터 10000까지의 수가 들어있는 natural에서 생성자가 있는 d(n)들을 모조리 빼준다.

출력할 때 순서대로 나와야 하니까 sorted로 정렬도 해준다.

그럼.. 생성자 없는 self_num set이 완성....

for문으로 self_num 하나씩.. 하나씩 출력하면 끝

 

728x90
728x90

문제

알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.

입력

첫째 줄에 알파벳 대소문자로 이루어진 단어가 주어진다. 주어지는 단어의 길이는 1,000,000을 넘지 않는다.

출력

첫째 줄에 이 단어에서 가장 많이 사용된 알파벳을 대문자로 출력한다. 단, 가장 많이 사용된 알파벳이 여러 개 존재하는 경우에는 ?를 출력한다.

예제 입력 1 복사

Mississipi

예제 출력 1 복사

?

예제 입력 2 복사

zZa

예제 출력 2 복사

Z

예제 입력 3 복사

z

예제 출력 3 복사

Z

예제 입력 4 복사

baaa

예제 출력 4 복사

A

words = input().upper()
unique_words = list(set(words)) 

cnt_list = []
for x in unique_words :
    cnt = words.count(x)
    cnt_list.append(cnt)  

if cnt_list.count(max(cnt_list)) > 1 :  
    print('?')
else :
    max_index = cnt_list.index(max(cnt_list)) 
    print(unique_words[max_index])

문제를 풀 때 입력받은 문자 중 중복되는 값을 제거한 리스트를 변수에 저장하고서 입력받은 문자열의 알파벳 개수를 세는데 이용했다. 빈 리스트를 생성하고서 개수를 센 수를 리스트에 추가하였고 최종적으로 수로 이루어진 리스트에서 가장 큰 값을 출력하였다. 이때, if-else 조건식을 이용해서 가장 큰 값의 개수가 여러 개인 경우 물음표를 출력하도록 했다. 

728x90

+ Recent posts