Python Basic Techniques
for Problem Solving
오랜만에 PS를 재개함에 따라 기억에서 사라진 테크닉들을 되살리기 위한 글입니다.
파이썬은 C++(혹은 C)와 입출력 방법, 리스트 활용 방법 등에서 차이가 있으므로 필요할 때마다 찾아보시면 좋습니다!
파이썬으로 입력 어떻게 받냐고 물어보면 이 글 링크 넘길거야..
혹시 또 다른 좋은 팁 있으면 알려주세요 :)
1. 입력
Input function: stdin.readline()
# 입력을 빠르게 받을 때
from sys import stdin
input() 대신 stdin.readline()으로 입력받는 것이 PS에서 유리하다.
이유는 다음과 같다.
1. stdin.readline()은 prompt(입력하라고 깜빡이는 그것)를 출력하지 않지만, input()은 prompt를 출력한다.
2. stdin.readline()은 한 번에 읽어서 버퍼에 저장, input()은 키보드로 입력할 때마다 한 번씩 데이터를 버퍼에 저장한다.
따라서 stdin.readline()을 사용해야 시간초과를 예방할 수 있다.
숫자를 입력받는 방법
주의할 점은 map()안에서 입력받는 경우 map object를 생성하므로, list로 변경할 때 반드시 list()로 묶어야 한다는 것이다.
stdin.readline()만 사용하는 경우는 그냥 대괄호로 묶거나, list()로 묶어도 상관없다.
# 숫자 하나 입력받을 때
# ex) 1
num = int(stdin.readline())
# 숫자 두개 이상을 개별적인 변수에 입력받을 때
# ex) 2 3
num1, num2, ... , num100 = map(int, stdin.readline().split())
# 숫자를 한 줄에 띄어쓰기로 구분하여 리스트로 입력받을 때
# ex) 2 3 5 7
num_list = list(map(int, stdin.readline().split()))
# 숫자를 한 줄에 띄어쓰기 없이 여러 개 입력받을 때
# ex) 2357
# 2357이 아니라 2, 3, 5, 7로 분리
num_list = list(map(int, list(stdin.readline().strip())))
# 숫자를 여러 줄에 하나씩 입력받을 때
# ex) 2
# 3
# 5
num_list = [int(stdin.readline()) for _ in range(num)]
# 2차원 리스트 바로 입력받기
# ex) 2 3 5
# 7 8 9
# 열의 길이가 num이며, 행은 input에 따라 결정된다.
num_list = [list(map(int, stdin.readline().split())) for _ in range(num)]
문자열을 입력받는 방법
문자열 하나 입력받을 때에는 list()나 대괄호로 묶을 필요가 없다.
만약, 문자열 하나를 문자 단위로 list에 넣어야 하는 경우라면 list()로 묶어주자.
# 문자열 1개 입력받기
# readline()으로 입력받으면 맨 뒤에 '\n'이 남으므로 strip()으로 제거
string = stdin.readline().strip()
# 문자열 여러 개 입력받기
string1, string2, ... , string100 = stdin.readline().split()
# 문자열을 한 줄에 띄어쓰기로 구분하여 여러 개 입력받을 때
string_list = stdin.readline().split()
# 문자열을 여러 줄에 여러 개 입력받을 때
string_list = [stdin.readline().strip() for _ in range(num)]
# 문자열을 문자 단위로 끊어서 리스트에 입력할 때
str_list = list(stdin.readline().strip())
stdin 대신 특정 파일로 입력을 redirection하는 방법
PS에서 testcase가 지나치게 길거나, customizing하는 경우 복사-붙여넣기 하는 것이 힘들 수 있다.
이럴 때에는 stdin(=키보드로 입력)을 텍스트 파일로 redirection하면 된다.
open()의 첫 번째 argument가 경로인데, 현재 돌리는 코드와 텍스트 파일의 위치가 동일하다면
현재 위치의 case.txt이므로 ./case.txt를 적고, 경로가 다른 경우 경로에 맞게 수정해주면 된다.
(경로를 지정하는 방법은 Linux를 공부하자!)
# 특정 파일로 stdin을 redirection
from sys import stdin
stdin = open('./case.txt', 'r') # .py와 case.txt 파일의 위치가 동일한 경우
중복된 입력을 피하는 방법
list 자료형 대신 set 자료형을 사용하면 중복된 입력을 피할 수 있으므로 위에서 list를 모두 set으로 바꾸면 된다.
다만 list처럼 인덱싱, 슬라이싱을 할 수 없음을 유의해야 한다.
set()에 입력받고, list()로 바꿔주면 다시 인덱싱, 슬라이싱을 정상적으로 할 수 있다.
그 외에도 기존의 list를 set으로 바꿨다가 다시 list로 바꿔주는 등으로 활용이 가능하다.
이때, 순서가 엉망이 될 수 있으니 정렬까지 해주는 것이 중요하다. (집합은 원소 간의 순서가 없다.)
2. 출력
리스트의 원소를 출력하는 방법
for loop를 이용하여 list의 원소를 출력하면 된다.
이때, for tmp in range(len(num_list))처럼 인덱스로 접근하는 방법은 지양하는 것이 좋다.
# 한 줄씩 리스트의 원소를 출력할 때
for tmp in num_list:
print(tmp)
# 한 줄에 리스트의 원소를 모두 출력할 때
print(*num_list)
print()의 옵션을 활용하는 방법
end, sep(seperator, 구분자), format, escape를 이용할 수 있다.
escape는 C++에서 사용한 것과 동일하므로 아래는 end, sep, format에 대한 설명이다.
# end: default값은 '\n'으로, print()를 사용하면 자동으로 개행되는 이유이다.
# 이를 수정하면 end character를 변경할 수 있다.
print('Hello', end='') # Hello 출력 이후 줄을 바꾸지 않음
# sep: default값은 ' '으로, print() 안에 parameter를 여러 개 넣었을 때 자동으로 띄어쓰기 되는 이유이다.
# 이를 수정하면 parameter간 separator를 변경할 수 있다.
print(*num_list, sep=',') # num_list의 원소를 콤마로 구분하여 모두 출력
# format: 특정 서식에 따라 문자열을 출력할 수 있다.
print('Case #{}: {}'.format(i+1, a+b)) # 첫 번째 {}에 i+1, 두 번째 {}에 a+b를 출력
3. list & string 관련
Initialization하는 방법
행과 열 크기를 반대로 할당하지 않도록 조심해야 하고, index는 zero-based numbering임을 유의해야 한다.
또한, dfs혹은 bfs에서 visit 처리를 위한 경우 n이 아니라 n+1만큼 할당하는 것이 편할 때도 있다.
(보통 0번 노드를 사용하지 않고, 1번 노드부터 n번 노드의 방문을 확인해야 하니까 !)
# 1차원 리스트 선언과 동시에 초기화
num_list = [0] * 100 # [0, 0, 0, ... , 0]
num_list = [[] for row in range(num)] # 원소가 리스트 (like 2-d)
# 2차원 리스트 선언과 동시에 초기화
num_list = [[0 for col in range(num)] for row in range(num)]
num_list = [[0] * 3 for _ in range(num)] # [[0, 0, 0], [0, 0, 0], ... , [0, 0, 0]]
num_list = [[] for col in range(num) for row in range(num)] # 원소가 리스트 (like 3-d)
# 3차원 리스트 선언과 동시에 초기화
num_list = [[[0 for col in range(num)] for row in range(num)] for channel in range(num)]
num_list = [[[{} for col in range(num)] for row in range(num)] for channel in range(num)] # 원소가 딕셔너리
Sorting하는 방법
문자열과 숫자를 정렬할 때 결과가 다를 수 있음을 조심해야 한다.
예를 들어, ['10', '9']와 [10, 9]를 각각 오름차순 정렬했을 때, 결과가 다르다. (문자열은 유니코드를 기반으로 정렬하기 때문)
sort()를 이용하면 원본이 수정되고, sorted()를 이용하면 원본이 수정되지 않는다.
간혹 string 안에서 alphabet을 정렬하기도 한다..
# 1차원 리스트 정렬
num_list.sort() # 오름차순
num_list.sort(reverse=True) # 내림차순
# lambda를 이용한 리스트 정렬
# lambda x: 이후 원하는 조건을 넣으면, 차례대로 정렬
num_list.sort(key=lambda x: x[0], -x[2]) # 0번인덱스 오름차순, 2번인덱스 내림차순
# 문자열 내부의 알파벳 정렬 (애너그램에서 mapping할 때 간혹 사용)
string = ''.join(sorted(string)) # word → dorw
Slicing하는 방법
list 혹은 string에서 원하는 부분만 떼어내서 활용할 수 있다.
예를 들면, 원하는 부분만 출력하거나, for loop에 넣거나 등등
# 리스트 혹은 문자열에서 특정 부분만 선택 (동일한 방법)
num_list = tmp_list[:-1] # 맨 뒤만 제외하고 선택
string = tmp[1:-1] # 맨 앞과 맨 뒤만 제외하고 선택
# 리스트 혹은 문자열 뒤집기 (동일한 방법)
num_list = num_list[::-1] # 리스트 뒤집기
string = string[::-1] # 문자열 뒤집기
# 정수도 뒤집을 수 있지만, 슬라이싱으로 뒤집으면 느리다.
# 따라서 %와 /를 이용하여 뒤집는 것이 좋다.
num = int(str(num)[::-1])
list 내의 원소들의 합, 평균, 최대, 최소값을 구하는 방법
파이썬 내장함수를 충실하게 이용해보자.
# 리스트 내 원소의 합을 구할 때
sum(num_list)
# 리스트 내 원소의 평균을 구할 때
sum(num_list) / len(num_list)
# 2차원 리스트 내에서 원소의 최대값을 구할 때
max_num = max(map(max, num_list)) # 최소값은 max 대신 min을 사용