문제 출처
[문제 요약]
승환이는 규현이에게 메시지를 받는다. 그런데 그냥 받는게 아닌 비밀 규칙을 적용해서 받는다.
우선 문자 메시지에 들어가는 글자는 알파벳 대문자와 공백으로 이루어져 있다.
글자는 아래처럼 십진수로 바꾼후 5자리 이진수로 바꾼다.
공백 = 0, A = 1, B = 2, ..., Y = 25, Z = 26
바뀐 이진수들은 시계방향의 소용돌이 패턴으로 행렬에 저장한다.
첫 줄에는 해석해야하는 행렬의 개수 (n) 를 받고 그 개수만큼 행렬을 입력을 받는다.
행렬의 행과 열, 그리고 소용돌이 패턴으로 저장된 행렬을 행 우선으로 한 줄로 읽은 것을 입력으로 받는다.
출력으로는 메시지를 프린트하면 된다.
추가적으로 행렬에서 남는칸은 0으로 채우고 만일 해석한 메시지의 끝에 공백이 있으면 공백은 제외하고 프린트 해야한다.
예를 들어 규현이가 보내는 메시지가 "ACM"이고, 행렬의 크기가 R=4, C=4라고 하면 아래처럼 메시지가 완성된다.
A = 00001, C = 00011, M = 01101
[풀이]
문제에 적힌대로 코드를 짜면 되는데 소용돌이 패턴을 만드는게 일이다.
코드로도 다소 복잡하게 되어서 이것보다 더 좋은 방식을 생각한다면 업데이트 할것 같다.
[row나 column index중 무엇을 어느쪽으로 바꿀지 정하는 변수: flag / direction]
우선 flag를 하나 정한다. 이건 row index를 바꿀지 column index를 바꿀지 결정하는 변수이다.
그리고 r_dir과 c_dir을 하나 만든다 (dir: direction (방향)의 약자). 이건 index를 바꿀때 어느 방향으로 바꿀지 결정하는 변수이다.
flag가 1이면 row index를 바꾸는거고 -1이면 column index를 바꾸는것이다.
r_dir도 1일때는 index를 1 더하고, r_dir가 -1일때는 index를 1 빼준다.
마찬가지로 c_dir도 1일때는 index를 1 더하고, r_dir가 -1일때는 index를 1 빼준다.
[행렬 소용돌이의 시작과 끝을 결정하는 변수: start / end]
어느 방향으로 바꿀지를 결정하는 함수에는 추가적으로 4개의 변수가 더 쓰인다.
index가 행렬의 끝에 다다르는걸 판별하기 위한 변수들이다.
여기서 숙지해야할 점은 r_start는 현재 가장 작은 index인 0보다 1 작다는것이고
r_end는 현재 가장 큰 index인 row_length (R-1) 보다 1 크다는 점이다. column도 마찬가지이다.
r_start는 -1, r_end는 R (row의 크기)
c_start도 -1, c_end는 C (column의 크기)
예를 들어 시작할때 column의 방향으로 index를 하나씩 더해주다가 index가 c_end - 1 (이 상황에서는 C-1가 되면)
방향 변환을 요약하면
[행렬 소용돌이의 크기가 작아짐에 따라 바뀌는 start / end]
마지막으로 r_start, r_end, c_start, c_end의 크기도 바꿔주어야 한다.
대신 바꾸는 타이밍이 중요한데 column의 index가 c_end - 1 에 다다른다고 해도, row가 r_end에 다다를때까지 유지시켜야한다.
하나 예시를 들기 위해 아래 그림을 다시 보자.
초반부에 column index가 c_end에 다다르기까지 r은 항상 0이다. r_start의 크기를 하나 더해준다. 그림을 참고하면 c_end가 c-1에 다다랐을때 이후로는 r이 0이 될 필요가 없어진다.
리밋 (start, end) 변화를 시계방향으로 요약하면
[유의할 점]
방향을 바꾸어주는것, 방향을 바꾸는 index의 경계의 값을 언제 바꾸는지 설계하는게 가장 주의할 점이다.
그 외의 주의할 점들은 아래 사항 정도인것 같다.
*string to binary
int('1111', 2)
15
[파이썬 3 코드]
# alp = A to Z, 0 for space
# convert into 5-digit binary number
def return_idx(r_idx, c_idx, flag, r_dir, c_dir):
if flag == 1 and r_dir == 1:
r_idx += 1
elif flag == 1 and r_dir == -1:
r_idx -= 1
elif flag == -1 and c_dir == 1:
c_idx += 1
else: # flag == -1 and c_dir == -1:
c_idx -= 1
return r_idx, c_idx
def check_edge(r_idx, c_idx, flag, r_dir, c_dir, r_str, c_str, r_end, c_end):
# 방향을 트는것
if r_idx == r_end:
c_end -= 1
flag = -1
r_dir = -1
r_idx -= 1
# index 변화
r_idx, c_idx = return_idx(r_idx, c_idx, flag, r_dir, c_dir)
elif r_idx == r_str:
c_str += 1
flag = -1
r_dir = 1
r_idx += 1
# index 변화
r_idx, c_idx = return_idx(r_idx, c_idx, flag, r_dir, c_dir)
elif c_idx == c_end:
r_str += 1
flag = 1
c_dir = -1
c_idx -= 1
# index 변화
r_idx, c_idx = return_idx(r_idx, c_idx, flag, r_dir, c_dir)
elif c_idx == c_str:
r_end -= 1
flag = 1
c_dir = 1
c_idx += 1
# index 변화
r_idx, c_idx = return_idx(r_idx, c_idx, flag, r_dir, c_dir)
return r_idx, c_idx, flag, r_dir, c_dir, r_str, c_str, r_end, c_end
def reorder(r, c, strnum):
lst = []
idx = c
# 소용돌이 matrix 형태로 저장.
# 다만 nested list는 아니고 column이 문자열인 형태
for i in range(r):
if i != r - 1:
lst.append(strnum[idx-c:idx])
else:
lst.append(strnum[idx-c:])
idx += c
orig_lst = []
# 변수들 설정
r_idx = 0
c_idx = 0
flag = -1 # row_order = 1, column_order = -1
r_dir = 1 # right = 1, left = -1
c_dir = 1
r_str = -1
r_end = r
c_str = -1
c_end = c
while len(orig_lst) != len(strnum):
orig_lst.append(lst[r_idx][c_idx])
# if flag == 1:
# return_r_idx
# else:
# return_c_idx
r_idx, c_idx = return_idx(r_idx, c_idx, flag, r_dir, c_dir)
r_idx, c_idx, flag, r_dir, c_dir, r_str, c_str, r_end, c_end = check_edge(r_idx, c_idx, flag, r_dir, c_dir, r_str, c_str, r_end, c_end)
# join the orig_lst into correct order strnum
return "".join(orig_lst)
def conv_strbin_to_int(digit5, n=5):
# 5자리 binary 수 -> 정수
sum = 0
to_add = 1
for i in reversed(range(n)):
if digit5[i] == '1':
sum += to_add
to_add = to_add << 1
return sum
def conv_int_to_char(num):
# 정수 -> 대문자 및 공백
# (ascii code에서 대문자 A가 65여서 모든 알파벳 수에다 64 더해주는것.)
if num == 0:
return " "
else:
return chr(num + 64)
def conv_ascii(orig_str: str, n = 5):
# 소용돌이를 풀어둔 binary 형태의 문자열 -> 메시지로 변환
count = n
res_str = []
while len(orig_str) > count:
_int = conv_strbin_to_int(orig_str[count-n:count])
_char = conv_int_to_char(_int)
count += n
res_str.append(_char)
if count == len(orig_str):
l_int = conv_strbin_to_int(orig_str[count-n:])
l_char = conv_int_to_char(l_int)
res_str.append(l_char)
return "".join(res_str)
def trim_end(res_str: str):
# 메시지의 공백을 삭제하는 함수
cut_idx = -1
for i in range(len(res_str)-1, 0, -1):
if res_str[i] != " ":
break
else:
cut_idx = i
if cut_idx == -1:
return res_str
else:
return res_str[:cut_idx]
if __name__ == "__main__":
# test case t
num = int(input())
msg_lst = []
for i in range(num):
r, c, str_num = input().split()
r = int(r)
c = int(c)
orig_str = reorder(r, c, str_num) # 이진수로 된 문자열 string
message = conv_ascii(orig_str) # 원래 문자열 리스트
trimmed = trim_end(message)
msg_lst.append(trimmed)
for msg in msg_lst:
print(msg)
[백준] 1676 팩토리얼 0의 개수 (Baekjoon Problem 1676: Count trailing 0's in factorial n) (0) | 2023.06.12 |
---|---|
[백준] 2545 팬케익 먹기 (Baekjoon Problem 2545: Eat Pancake) (0) | 2023.06.12 |
[백준] 2740 행렬 곱셈 (Baekjoon Problem 2740: Matrix Multiplication) (0) | 2023.06.04 |
[백준] 1331 나이트 투어 (Baekjoon Problem 1331: Knight Tour) (0) | 2023.06.04 |
[백준] 2097 조약돌 (Baekjoon Problem 2097: Pebbles) (0) | 2023.06.04 |
댓글 영역