from collections import defaultdict
s = input()
odd = 0
alphabets = defaultdict(int)
ans, mid = '', ''
for i in s:
alphabets[i] += 1
if len(s) % 2 == 0:
for alphabet, cnt in sorted(alphabets.items()):
ans += alphabet*(cnt//2)
if cnt % 2 == 1:
odd += 1
break
if odd > 0:
print("I'm Sorry Hansoo")
else:
print(ans+mid+ans[::-1])
else:
for alphabet, cnt in sorted(alphabets.items()):
ans += alphabet*(cnt//2)
if cnt % 2 == 1:
odd += 1
mid = alphabet
if odd == 2:
break
if odd > 1:
print("I'm Sorry Hansoo")
else:
print(ans+mid+ans[::-1])
이상하게 빨리 푸는 방법이 생각나지 않은 문제였습니다. 어떻게든지 인덱스로 해결해야겠다는 그런 생각이 자꾸 들어서 왼쪽 절반을 뒤집으면 오른쪽이 된다는 게 생각이 안나 쓸데없이 반복문을 많이 사용했던 문제입니다. 아래에서 다른 분의 코드를 참고해 최적화한 코드도 소개할 예정이니 일단은 위의 코드를 기준으로 설명을 진행하겠습니다.
문제 자체의 난이도가 그렇게 높지는 않습니다. 문제가 원하는 것은 입력을 받아서 회문을 만들 수 있냐는 것인데, 회문을 만들지 못하는 입력에서는 다른 문장을 출력하는 것이 해결해야할 것들입니다. 즉 두가지가 합쳐진 셈이죠. 회문을 만드는 것과, 회문이 안되는 조건을 판단하는 것.
저 같은 경우는 회문이 안되는 조건을 판단할 때 주어진 입력의 길이가 짝수이면 문자 하나라도 홀수일 때 회문이 안된다는 것과, 입력의 길이가 홀수이면 문자가 둘 이상이 홀수일 때 회문이 안된다는 조건으로 나눴습니다. 그래서 딕셔너리를 탐색하면서 개수가 홀수인 알파벳의 개수를 체크하도록 조건문을 작성해서 회문이 되지 않는 경우를 판단했습니다.
회문을 만드는 것은 나온 개수의 절반(정확히는 2로 나눴을 때의 몫)만큼 정답 문자열에 추가해준 다음 홀수가 나와서 중간에 들어가야 할 친구(입력이 짝수라면 중간에 들어갈 문자열이 없기 때문에 입력이 홀수일 때나 짝수일 때 다 적용가능)를 집어넣고 정답 문자열을 뒤집어서 다시 추가하는 방식을 사용했습니다. 처음에는 이게 생각이 안나서 딕셔너리의 키를 뒤집어서 반복문을 진행하는 방식을 사용했었는데 다 풀고 보니 너무 비효율적이더라구요.
풀긴 풀었지만 여전히 코드도 비효율적인 것 같고 조건문이 너무 많아서 가독성이 떨어지는 것 같아 다른 분의 코드를 또 참고해서 수정해봤습니다.
from collections import defaultdict
s = input()
alphabets = defaultdict(int)
ans, mid = '', ''
for i in s:
alphabets[i] += 1
for alphabet, cnt in sorted(alphabets.items()):
ans += alphabet * (cnt//2)
if cnt % 2 == 1:
if mid != '':
print("I'm Sorry Hansoo")
break
else:
mid = alphabet
else:
print(ans + mid + ans[::-1])
일단 스크롤을 내리지 않아도 한 눈에 보인다는 점이 매우 좋습니다. 코드를 설명 드리면, 회문을 만드는 과정 자체는 크게 다르지 않는데 회문이 되지 않는 조건을 판단하는 부분이 상당히 짧고 좋습니다. 먼저 딕셔너리의 key 와 value를 반복문을 통해 하나씩 탐색하면서 cnt가 홀수일 때 이미 중간 문자열이 채워져 있다면 홀수가 하나가 아니라는 뜻이니까 반복문을 종료하고 중간 문자열이 비어있다면 채워주는 방식을 사용했습니다.
글로 풀어서 쓰면 위나 아래나 설명이 비슷한거 같은데 결과물은 이렇게나 차이가 나는 걸 볼 수 있죠. 정말 다들 아이디어가 대단한 것 같습니다.
그리고 제가 정말 소개하고 싶었던 것은 마지막 줄인데요. if와 짝을 이루지 않는 else가 하나 보이실텐데요. 저는 처음에 이게 오타인데 깜빡하고 그 분이 그냥 올리셨구나 싶었습니다만 그런게 아니더라구요. 저렇게 단독으로 else를 쓰는 경우는 해당 반복문을 진행하면서 break를 만나지 않고 정상적으로 반복문이 끝났을 때 else구문을 실행시키는 방법이라고 합니다. 가끔 문제를 풀다가 들여쓰기 레벨이 맞지 않을 때 break를 어떻게 해야할지 else를 어떻게 해야할지 잘 모르겠어서 그냥 함수로 짜버렸던 경험이 있는데 이런식으로 할 수 있다는게 너무 신기했고, 다른 분들께도 꼭 소개해드리면 좋겠다 싶어서 한 번 작성해봤습니다.
추가로 defaultdict 자료형은 딕셔너리의 value를 기본값으로 정해둘 수 있는 자료형입니다. dictionary 자료형의 서브자료형으로 원래 dict 자료형을 선언할 때 key와 value를 쌍으로 선언해줘야 하는데, defaultdict를 사용하면 dict내에 자료를 추가할 때 value의 default 값이 정해져 있으므로 이런식으로 value를 계속 키우는 경우에 쓸만하겠죠. 만일 defaultdict를 사용하지 않고 유저들의 접속 횟수를 체크하는 시스템을 짠다고 가정하면, 접속 기록이 생길 때 마다 해당 유저가 기존에 접속한 기록이 있는지를 체크하고 있으면 값을 올리고 없으면 신규등록을 하는 과정을 다 코드로 짜야겠지만 defaultdict를 사용하면 없으면 자동으로 신규등록이 될거고 있으면 key로 접근이 가능하니까 value만 키워주면 되겠죠. 코드 자체도 짧아지고 개발하는 입장에서도 약간 편해질 것 같습니다.
'Problem Solving > BOJ' 카테고리의 다른 글
[BOJ][Python]1291 풀이 (0) | 2023.07.08 |
---|---|
[BOJ][Python]1270 풀이 (0) | 2023.07.07 |
[BOJ][Python]1166 풀이 (2) | 2023.07.04 |
[BOJ][Python]1072 풀이 (0) | 2023.07.03 |