3주차 | 문제 2. 폴더폰 자판
문제
10년 전, 구름이가 처음으로 구매했던 휴대폰은 폴더 폰이다. 폴더 폰의 자판은 최근의 휴대폰의 입력 방식과는 차이가 있다. 폴더 폰의 자판 형식은 아래와 같다.
9개의 입력 버튼으로 이루어져 있으며, 각 버튼의 첫 번째 숫자로 버튼을 부를 수 있다. 한 번 버튼을 누르면 숫자가 입력이 되고, 여러 번 누르면 자판에 보이는 문자들로 순서대로 바뀐다. 예를 들면 [버튼 5]를 한 번 누르면 5가 입력이 되지만, [버튼 5]를 세 번 누르면 k가 입력된다. 또 [버튼 5]를 5번 이상 누르게 되면 다시 처음 숫자가 입력된다. [버튼 7] 이나 [버튼 1]은 6번 이상일 때 동일하게 된다.
10년 전 구름이가 구매한 폴더 폰을 오랜만에 꺼내서 작동을 했더니, 휴대폰이 고장이 났다는 사실을 발견했다. 같은 [버튼]을 여러 번 눌렀을 때, 문자가 바뀌지 않고 숫자가 그대로 하나씩 입력되는 것이다. 구름이를 도와 주어진 수열을 바탕으로 구름이가 실제로 입력하려고 했던 문장을 출력하시오.
입력
첫째 줄에 수열의 길이 n(1≤n≤$10^5$)가 주어진다.
둘째 줄에 공백 없이 한 줄로 수열 s(1≤$s_i$≤9)가 주어진다.
출력
s를 원래 문장으로 변환한 결과를 출력하시오.
오답 1 - Counter 활용 / 런타임 에러 X / 통과하지 못한 케이스 존재
import sys
import collections
n=int(sys.stdin.readline().rstrip()) # 수열의 길이(=버튼을 누를 횟수)
s=sys.stdin.readline().rstrip() # 수열(=누를 버튼)
# 인덱싱 편의를 위해 0번 인덱스 위치에 빈 튜플 삽입
phone=[(),(1,'.','?','!'),(2,'A','B','C'),(3,'D','E','F'),
(4,'G','H','I'),(5,'J','K','L'),(6,'M','N','O'),
(7,'P','Q','R','S'),(8,'T','U','V'),(9,'W','X','Y','Z')]
dict={}
dict=collections.Counter(s)
for k,v in dict.items():
if v>len(phone[int(k)]):
v=v%len(phone[int(k)])
print(phone[int(k)][v-1],end='')
파이썬에서 Counter는 데이터의 개수를 셀 때 유용하게 사용할 수 있다. 이러한 점을 이용하여 입력받은 수열에 대해 각 숫자별 입력된 횟수를 Counter를 통해 구해보려고 했다. 하지만 이 코드에는 문제점이 존재한다.
예를 들어, 4445555664444와 같은 수열이 입력되었을 때 4는 처음에 입력받은 횟수(3번)과 마지막에 입력받은 횟수(4번)을 각각 나눠서 세주어야 하는데 Counter를 사용하면 한번에 4의 개수를 세어 7개로 오류가 발생한다.
오답 2 - count() 함수 활용 / 런타임 에러
import sys
input = sys.stdin.readline
n=int(input()) # 수열의 길이(=버튼을 누를 횟수)
s=input() # 수열(=누를 버튼)
phone=[(),(1,'.','?','!'),(2,'A','B','C'),(3,'D','E','F'),
(4,'G','H','I'),(5,'J','K','L'),(6,'M','N','O'),
(7,'P','Q','R','S'),(8,'T','U','V'),(9,'W','X','Y','Z')]
sen=[]
for i in range(n):
if i==n:
break
else:
if s[i]==s[i+1]:
continue
else: # 현재 문자가 다음 문자와 다르면
sen.append((int(s[i]),s.count(s[i]))) # (문자, 횟수) tuple 추가
for i in range(len(sen)):
if sen[i][1]>len(phone[sen[i][0]]): # 자판의 길이보다 길면 나머지 계산
sen[i][1]%=len(phone[sen[i][0]])
print(phone[sen[i][0]][sen[i][1]-1],end='')
오답 1 코드에서의 문제점을 해결하기 위해 문자가 달라지는 부분에서 해당 문자의 개수를 계산하는 count() 함수를 활용하였다.
하지만 어떠한 이유에서인지 런타임 에러가 발생하였다. n의 최대 값이 $10^5$이기 때문에 발생한 것으로 추측되는데 구름 정답코드와 비교해보았지만 아직까지 런타임 에러가 발생하는 정확한 이유를 파악하지 못했다.
오답 3 - 구름 해설 참고 / 통과하지 못한 케이스 존재
import sys
input = sys.stdin.readline
n=int(input()) # 수열의 길이(=버튼을 누를 횟수)
s=input() # 수열(=누를 버튼)
phone=[(),(1,'.','?','!'),(2,'A','B','C'),(3,'D','E','F'),
(4,'G','H','I'),(5,'J','K','L'),(6,'M','N','O'),
(7,'P','Q','R','S'),(8,'T','U','V'),(9,'W','X','Y','Z')]
c = 0
for i in range(n):
if i == n:
break
else:
if s[i+1] == s[i]: # 다음 문자랑 같으면 c+1
c += 1
continue
else: # 다음 문자랑 다르면 지금까지의 c를 가지고 출력할 문자 찾기
c %= len(phone[int(s[i])]) # i번째에 입력된 버튼을 dic에서 찾고 cnt를 dic의 길이로 나눈 나머지로 바꿔줌(dic의 길이가 4일때 6번 눌리면 처음부터 돌아와야 하므로)
print(phone[int(s[i])][c],end='')
c = 0 # 다음 문자의 입력 횟수를 계산하기 위해 c를 0으로 초기화
최대한 내가 작성한 코드를 살려보고자 정답 코드를 참고하여 수정해보았지만 통과하지 못한 케이스가 존재한다. 혹시 이유를 아는 분이 댓글로 잘못된 부분을 알려주시면 그쪽 방향으로 절을 하겠다.
구름 정답 코드
import sys
input = sys.stdin.readline
n = int(input())
dic = {
1 : "1.,?!",
2 : "2ABC",
3 : "3DEF",
4 : "4GHI",
5 : "5JKL",
6 : "6MNO",
7 : "7PQRS",
8 : "8TUV",
9 : "9WXYZ",
}
command = input()
ans = '' # 출력할 문자열을 담을 문자열 ans
cnt = 0
for i in range(n):
if i == n:
break
else:
if command[i+1] == command[i]: # 다음 문자랑 같으면 cnt+1
cnt += 1
continue
else: # 다음 문자랑 다르면 지금까지의 cnt를 가지고 출력할 문자 찾기
cnt %= len(dic[int(command[i])]) # i번째에 입력된 버튼을 dic에서 찾고 cnt를 dic의 길이로 나눈 나머지로 바꿔줌(dic의 길이가 4일때 6번 눌리면 처음부터 돌아와야 하므로)
ans += dic[int(command[i])][cnt] # 해당 버튼에서 출력될 문자를 ans에 추가하고
cnt = 0 # 다음 문자의 입력 횟수를 계산하기 위해 cnt를 0으로 초기화
print(ans)
내가 쓴 코드와의 차이점을 살펴보면 다음과 같다.
1. 자판을 딕셔너리로 정의했다는 점
2. 해당 문자를 바로 출력하는 것이 아닌 ans라는 문자열에 추가하는 방식으로 출력했다는 점
2번의 경우 같은 방식으로 시도해보았으나 역시나 실패 케이스가 존재했다. 딕셔너리로 정의해야만 모든 케이스를 통과할 수 있는 걸까?