알아두면 좋은 코딩 테스트 Skills

변현섭·2024년 10월 4일
5
post-thumbnail

1. Utilty 관련 메서드

간혹 낮은 난이도의 문제에서 유틸리티 관련 메서드에 대해 물어보는 경우가 있기 때문에, 이러한 메서드도 미리 알아두면 쉬운 문제를 빠르게 푸는 데에 도움이 될 것이다.

1) 오늘의 날짜 또는 시간 출력하기

from datetime import date, datetime, timedelta

# 오늘의 날짜
print(date.today()) # 2024-08-29

# 오늘의 날짜 + 현재 시간
print(datetime.now()) # 2024-08-29 00:22:00.635581

# 출력 형식 지정
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) # 2024-08-29 00:22:00

# 현재 시간으로부터 2시간 30분 30초가 지난 시점
print(datetime.now() + timedelta(hours=2, minutes=30, seconds=30)) # 2024-08-29 02:52:30.635581

※ 관련 문제
>> 백준 10699번

## 정답 코드 ##
from datetime import date

print(date.today())

2) 대소문자 확인 및 변환하기

① 대소문자 확인

S = input().strip() # HeLlO 입력

for s in S:
	if s.isupper():
		print("U", end=' ')
	if s.islower():
		print("L", end=' ')
        
# U L U L U  출력

② 대소문자 변환

S = input().strip() # HeLlO 입력

# <대-소 변환>
print(S.swapcase()) # hElLo 출력

# <소문자 변환>
print(S.lower()) # hello 출력

# <대문자 변환>
print(S.upper()) # HELLO 출력

3) 알파벳 및 숫자 확인하기

※ 관련 문제
"q1/w2e*3r^4"와 같이 숫자, 알파벳, 특수문자가 혼재되어 있는 문자열이 주어진다. 주어진 문자열에서 알파벳은 다음 알파벳으로, 숫자는 +1, 특수문자는 그대로 유지하여 결과 문자열을 출력하라. (단, 알파벳이 z인 경우는 a로, 숫자가 9인 경우는 0을 사용한다.)

위 문제를 풀이하려면 전체 문자열을 순회하면서, 현재 문자가 알파벳인지 숫자인지를 파악할 수 있어야 할 것이다. 이 때 사용할 수 있는 메서드에는 isalpha(), isdigit()가 있다. 한 가지 주의해야 할 점은 두 메서드 모두 dot operator 형식으로 사용해야 한다는 것이다.

① isalpha()

  • 문자가 알파벳인지 또는 문자열이 모두 알파벳으로만 구성되어 있는지 여부를 반환한다.
a = 'q1w2e3r4'
b = '1234'
c = 'qwer'
 
print(a.isalpha()) # False
print(b.isalpha()) # False
print(c.isalpha()) # True

② isdigit()

  • 문자가 숫자인지 또는 문자열이 모두 숫자로만 구성되어 있는지 여부를 반환한다.
a = 'q1w2e3r4' 
b = '1234'
c = 'qwer'
 
print(a.isdigit()) # False 
print(b.isdigit()) # True
print(c.isdigit()) # False

③ isalnum()

  • 문자가 알파벳 및 숫자인지 또는 문자열이 모두 알파벳과 숫자로만 구성되어 있는지 여부를 반환한다.
  • 위 문제를 풀이할 때, 직접적으로 사용하지는 않지만 함께 알아둘 것을 권장한다.
a = 'q1w2e3r4'
b = 'qwer'
c = '1234'
d = '***'
e = 'q1w2e3r4/'

print(a.isalnum()) # True
print(b.isalnum()) # True
print(c.isalnum()) # True
print(d.isalnum()) # False
print(e.isalnum()) # False

따라서, 위 문제는 아래와 같이 풀이할 수 있을 것이다.

S = 'q1/w2e*3r^4'

result = []
    
for char in S:
        if char.isdigit(): # 숫자인 경우
            # 9일 때는 0으로 순환
            result.append(str((int(char) + 1) % 10))

        elif char.isalpha(): # 알파벳인 경우
            # 'z' 또는 'Z'는 'a' 또는 'A'로 순환
            if char == 'z':
                result.append('a')
            elif char == 'Z':
                result.append('A')
            else:
                result.append(chr(ord(char) + 1))
        else:
            result.append(char)  # 다른 문자는 그대로 추가
            
print(''.join(result))

2. 수학적 연산

1) 거듭제곱 구하기

pow(a, b, c)를 이용하면, pow(a, b) % c보다 더 빠르게 값을 구할 수 있다.

※ 관련 문제
>> 백준 1629번

## 정답 코드 ##
import sys
input = sys.stdin.readline

a, b, c = map(int, input().split())

print(pow(a, b, c))

2) 순열 및 조합 Iterator

파이썬의 itertools 모듈을 사용하면, 순열 또는 조합에 대한 Iterator를 얻을 수 있으며, Iterator의 사용법은 아래와 같다.

from itertools import permutations, combinations, combinations_with_replacement

lst = ['A', 'B', 'C']

# 3개의 원소로 구성할 수 있는 순열을 순회 -> 6만큼의 경우의 수 존재
for i in permutations(lst):
    print(i)
print()

# 3개의 원소 중 2개를 선택한 조합을 순회 -> 3만큼의 경우의 수 존재
for i in combinations(lst, 2):
    print(i)
print()

# 3개의 원소 중 2개를 중복 선택한 조합을 순회 -> 6만큼의 경우의 수 존재 
for i in combinations_with_replacement(lst, 2):
    print(i)

## 출력 ##
('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')

('A', 'B')
('A', 'C')
('B', 'C')

('A', 'A')
('A', 'B')
('A', 'C')
('B', 'B')
('B', 'C')
('C', 'C')

※ 관련 문제
>> 백준 15686번

## 정답 코드 ##
import sys
input = sys.stdin.readline
from itertools import combinations

N, M = map(int, input().split())
graph = [list(map(int, input().split())) for i in range(N)]

house = [] # 집의 좌표
chicken = [] # 치킨집의 좌표

for i in range(N):
    for j in range(N):
        if graph[i][j] == 1: # 집인 경우
            house.append((i, j))
        if graph[i][j] == 2: # 치킨집인 경우
            chicken.append((i, j))

result = sys.maxsize # 최소 값을 구하기 위해 매우 큰 값으로 초기화

for comb in combinations(chicken, M): # 치킨집 리스트에서 M개를 선택하는 경우의 수를 순회
    total_dist = 0 # 모든 집의 치킨 거리의 합

    for h in house:
        house_chicken_dist = sys.maxsize 
        for j in range(M): # 각 집과 가장 가까운 치킨집까지의 거리를 구함 (거리는 |r1-r2| + |c1-c2|로 구함)
            house_chicken_dist = min(house_chicken_dist, abs(h[0] - comb[j][0]) + abs(h[1] - comb[j][1]))

        total_dist += house_chicken_dist 

    result = min(result, total_dist) # 각 조합마다 구해진 치킨 거리 값중 최소가 되는 값으로 업데이트 

print(result)

3) 몫과 나머지 동시에 구하기

몫과 나머지를 동시에 구하기 위해 내장 함수인 divmod() 메서드를 활용할 수 있다.

quotient, remainder = divmod(10, 3)

print(quotient) # 3
print(remainder) # 1

3. 집합 연산 및 관련 메서드

1) 집합 자료구조 기본 메서드

S = set()

S.add(1) # 원소 삽입
S.remove(1) # 원소 삭제
S.discard(1) # 해당 원소가 존재하지 않아도 에러가 발생하지 않음
S.clear() # 공집합

참고로, 리스트를 바로 집합에 삽입하는 것은 불가하다. 따라서, 집합에 리스트를 삽입해야 하는 경우, 반드시 먼저 튜플로 변환해야 한다.

S.add(tuple(lst))

2) 집합 연산 조건식

집합 연산을 사용하여 if문의 조건식을 작성하는 경우, 아래와 같은 연산자가 사용된다.

  • 교집합: &
  • 합집합: |
  • 차집합: -
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

# 교집합이 공집합이 아니거나, 합집합의 크기가 6보다 크다면
if (A & B) or (len(A | B) > 6): 
    ...

3) 집합 연산 메서드

  • 교집합: intersection
  • 합집합: union
  • 차집합: difference
set1 = set1.union(set2) # set1과 set2의 합집합
set1 = set1.intersection(set2) # set1과 set2의 교집합
set1 = set1.difference(set2) # set1과 set2의 차집합

※ 관련 문제
>> 백준 1043번

## 정답 코드 ##
import sys
input = sys.stdin.readline

N, M = map(int, input().split())
know = set(map(int, input().split()[1:]))
parties = []

for i in range(M):
    parties.append(set(map(int, input().split()[1:])))

for i in range(M):
    for party in parties:
        if party & know:
            know = know.union(party)

cnt = 0

for party in parties:
    if not (party & know):
        cnt += 1

print(cnt)

4. BFS 관련 주의사항

① BFS로 최단 거리를 구하는 문제

  • 예를 들어 순간 이동을 하는 데에는 0초, 앞으로 한 칸 또는 뒤로 한 칸 이동하는 데에는 1초가 걸린다고 하자.
  • 이러한 문제에서는 반드시 Deque에 순간 이동하는 경우를 가장 먼저 삽입해야 최단 거리를 제대로 구할 수 있게 된다.

※ 관련 문제
>> 백준 13549번

## 정답 코드 ##
import sys
input = sys.stdin.readline
from collections import deque

N, K = map(int, input().split())
visited = [-1] * (100001)

def bfs(V):
    deq = deque()
    deq.append(V)
    visited[V] = 0

    while deq:
        V = deq.popleft()
        if V == K:
            print(visited[V])
            exit()

        if V <= 50000 and visited[2 * V] == -1:
            deq.append(2 * V)
            visited[2 * V] = visited[V]
        
        if V > 0 and visited[V - 1] == -1:
            deq.append(V - 1)
            visited[V - 1] = visited[V] + 1
        
        if V < 100000 and visited[V + 1] == -1:
            deq.append(V + 1)
            visited[V + 1] = visited[V] + 1

bfs(N)

② 두 가지 연산을 이용해서 숫자 A를 B로 변환하는 문제

  • BFS로 풀이할 수 있다.
  • 최소 연산의 횟수를 구하기 위해, 현재까지의 연산 횟수를 deque에 현재 노드와 함께 삽입해야 한다.

※ 관련 문제
>> 백준 16953번

## 정답 코드 ##
A, B = map(int, input().split())

def bfs(V, cnt):
    deq = deque()
    deq.append((V, cnt))

    while deq:
        V, cnt = deq.popleft()
        if V == B:
            print(cnt)
            exit()
            
        elif V > B:
            continue

        deq.append((2 * V, cnt + 1))
        deq.append((V * 10 + 1, cnt + 1))

    print(-1)
        

bfs(A, 1)

5. Counter

주어진 문자열에서 문자의 개수를 Dictionary에 저장해야 하는 상황은 매우 빈번하다. 이러한 상황에선, 아래와 같이 직접 Dictionary를 정의하여 풀이할 수 있다.

dict = {}
S = "1325421134122427489"
for s in S:
        if s in dict:
            dict[s] += 1
        else:
            dict[s] = 1

그러나, 정의해야할 Dictionary가 여러 개인 경우, 코드가 길어지는 문제가 발생한다. 따라서, 이러한 상황에선 Counter 모듈을 활용할 것을 권장한다.

from collections import Counter

S1 = "1325421134122427489"
S2 = "85151221345232323"
S3 = "154643121549"

dict1 = Counter(S1)
dict2 = Counter(S2)
dict3 = Counter(S3)

6. 성능 최적화

① 리스트 순회

  • 실행시간 비교: 인덱스를 이용한 순회 > 원소 직접 순회
  • 두 방식 모두 사용이 가능하다면, 원소를 직접 순회하는 방식을 사용해야 한다.
lst = [x for x in range(N)]

# <성능 개선 >
for i in range(N):
	print(lst[i])

# <성능 개선 > 
for i in lst:
	print(i)

② if-elif-else 구문

  • 실행시간 비교: if-elif > if-else
  • 불필요한 elif는 반드시 else로 대체하여 사용한다.
num = int(input())

# <성능 개선 >
if num % 2 == 0:
	pint('even')
elif num % 2 == 1:
	print('odd')
    
# <성능 개선 >    
if num % 2 == 0:
	pint('even')
else:
	print('odd')

얼핏 보기에는 성능 상의 큰 차이가 없어보이는 로직이지만, 성능에 따라 차등 배점을 적용하는 문제에서는 성공/실패를 결정 짓기도 하는 중요한 요인이 된다. 그러므로, 처음부터 위와 같은 방식으로 코드를 짜는 습관을 들여두는 것이 좋다.

profile
LG전자 Connected Service 1 Unit 연구원 변현섭입니다.

0개의 댓글