Kakao, 2021 카카오 블라인드 코딩 테스트, 신규 아이디 추천
Series: 알고리즘 문제풀이
알고리즘 문제풀이contains 12
- 1.Kakao, 2021 카카오 블라인드 코딩 테스트, 신규 아이디 추천
- 2.백준(Baekjoon Online Judge) - 2839번(설탕 배달) 문제 풀이
- 3.백준(Baekjoon Online Judge) - 11399번(ATM) 문제 풀이
- 4.백준(Baekjoon Online Judge) - 11047번(동전 0) 문제 풀이
- 5.백준(Baekjoon Online Judge) - 1931번(회의실 배정) 문제 풀이
- 6.백준(Baekjoon Online Judge) - 5585번(거스름돈) 문제 풀이
- 7.백준(Baekjoon Online Judge) - 1541번(잃어버린 괄호) 문제 풀이
- 8.백준(Baekjoon Online Judge) - 2217번(로프) 문제 풀이
- 9.백준(Baekjoon Online Judge) - 1946번(신입 사원) 문제 풀이
- 10.백준(Baekjoon Online Judge) - 10162번(전자레인지) 문제 풀이
- 11.백준(Baekjoon Online Judge) - 1339번(단어 수학) 문제 풀이
- 12.백준(Baekjoon Online Judge) - 4796번(캠핑) 문제 풀이
- 더보기
1. 문제 설명
카카오에 입사한 신입 개발자 네오는 "카카오계정개발팀"에 배치되었다. 네오가 맡게 된 업무는 카카오 서비스에 가입하는 유저들의 아이디를 생성하고 관리하는 일이었다.
네오에게 주어진 첫 번째 업무는 신규 유저가 카카오 아이디 규칙에 맞지 않는 아이디를 입력했을 때, 입력된 아이디와 유사하면서도 규칙에 맞는 아이디를 추천해주는 프로그램을 개발하는 것이었다.
카카오 아이디 규칙은 다음과 같다.
- 아이디의 길이는 3자 이상 15자 이하여야 한다.
- 아이디는 알파벳 소문자, 숫자, 빼기(
-), 밑줄(_), 마침표(.) 문자만 사용할 수 있다. - 단, 마침표(
.)는 처음과 끝에 사용할 수 없으며, 연속으로 사용할 수도 없다.
네오는 신규 유저가 입력한 아이디가 위 규칙에 맞는지 검사하고, 규칙에 맞지 않는 경우 새로운 아이디를 추천해주려고 한다. 이때 추천 아이디는 총 7단계의 순차적인 처리 과정을 거쳐 만들어진다.
신규 유저가 입력한 아이디를 new_id라고 할 때, 처리 과정은 다음과 같다.
- 1단계 :
new_id의 모든 대문자를 대응되는 소문자로 치환한다. - 2단계 :
new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거한다. - 3단계 :
new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환한다. - 4단계 :
new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거한다. - 5단계 :
new_id가 빈 문자열이라면,new_id에"a"를 대입한다. - 6단계 :
new_id의 길이가 16자 이상이면,new_id의 첫 15개 문자를 제외한 나머지 문자들을 모두 제거한다. 만약 제거 후 마침표(.)가new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거한다. - 7단계 :
new_id의 길이가 2자 이하라면,new_id의 마지막 문자를new_id의 길이가 3이 될 때까지 반복해서 끝에 붙인다.
예를 들어 new_id 값이 "...!@BaT#*..y.abcdefghijklm"이라면, 위 7단계를 거치면서 값은 다음과 같이 변경된다.
1단계에서는 대문자 B와 T가 각각 소문자 b와 t로 바뀐다.
"...!@BaT#*..y.abcdefghijklm"
→ "...!@bat#*..y.abcdefghijklm"2단계에서는 사용할 수 없는 문자 !, @, #, *가 제거된다.
"...!@bat#*..y.abcdefghijklm"
→ "...bat..y.abcdefghijklm"3단계에서는 연속된 마침표가 하나의 마침표로 치환된다.
"...bat..y.abcdefghijklm"
→ ".bat.y.abcdefghijklm"4단계에서는 아이디의 처음에 위치한 마침표가 제거된다.
".bat.y.abcdefghijklm"
→ "bat.y.abcdefghijklm"5단계에서는 아이디가 빈 문자열이 아니므로 변화가 없다.
"bat.y.abcdefghijklm"
→ "bat.y.abcdefghijklm"6단계에서는 아이디의 길이가 16자 이상이므로, 처음 15자를 제외한 나머지 문자가 제거된다.
"bat.y.abcdefghijklm"
→ "bat.y.abcdefghi"7단계에서는 아이디의 길이가 2자 이하가 아니므로 변화가 없다.
"bat.y.abcdefghi"
→ "bat.y.abcdefghi"따라서 신규 유저가 입력한 new_id가 "...!@BaT#*..y.abcdefghijklm"일 때, 네오의 프로그램이 추천하는 새로운 아이디는 "bat.y.abcdefghi"가 된다.
2. 문제
신규 유저가 입력한 아이디를 나타내는 new_id가 매개변수로 주어진다.
이때 네오가 설계한 7단계 처리 과정을 거친 후의 추천 아이디를 반환하도록 solution 함수를 완성해야 한다.
3. 제한사항
new_id는 길이 1 이상 1,000 이하인 문자열이다.new_id는 알파벳 대문자, 알파벳 소문자, 숫자, 특수문자로 구성되어 있다.new_id에 나타날 수 있는 특수문자는-_.~!@#$%^&*()=+[{]}:?,<>/로 한정된다.
4. 입출력 예시
| no | new_id | result |
|---|---|---|
| 예1 | "...!@BaT#*..y.abcdefghijklm" | "bat.y.abcdefghi" |
| 예2 | "z-+.^." | "z--" |
| 예3 | "=.=" | "aaa" |
| 예4 | "123_.def" | "123_.def" |
| 예5 | "abcdefghijklmn.p" | "abcdefghijklmn" |
5. 문제 풀이
이 문제는 주어진 7단계 규칙을 순서대로 구현하면 되는 문제이다. 저는 처음 문제를 풀 때 최대한 기본적인 문자열 처리 방식에 충실해서 코드를 작성했다.
먼저 소문자 알파벳, 사용 가능한 기호, 숫자를 flags 리스트에 담아두었다. 이후 new_id를 순차적으로 가공하면서 문제에서 요구하는 조건을 하나씩 처리했다.
1단계에서는 lower() 함수를 사용해 입력받은 new_id를 모두 소문자로 변환했다.
2단계에서는 new_id 문자열을 순회하면서 사용할 수 없는 문자를 제거했다. 이때 문자열을 리스트로 변환한 뒤 다시 집합으로 변환하여 중복을 제거했고, 해당 문자가 flags에 포함되어 있지 않다면 replace() 함수를 사용해 빈 문자열로 바꿔주었다.
3단계에서는 마침표의 연속 여부를 확인하기 위해 point_cnt 변수를 사용했다. new_id를 순회하면서 마침표가 등장하면 point_cnt를 증가시키고, 마침표가 2번 이상 연속되는 경우 해당 부분을 하나의 마침표로 치환했다.
4단계에서는 new_id의 처음이나 끝에 마침표가 있는지 확인했다. 문자열의 길이가 0이 아니면서 첫 번째 문자가 마침표라면 첫 문자를 제거했고, 마지막 문자가 마침표라면 마지막 문자를 제거했다.
5단계부터 7단계까지는 문자열 길이에 따라 처리했다. 가공된 new_id의 길이가 0이라면 최종적으로 "aaa"가 되도록 처리했고, 길이가 16 이상이라면 처음부터 15개 문자만 남겼다. 이때 남은 문자열의 마지막 문자가 마침표라면 다시 제거했다. 마지막으로 길이가 2 이하라면 전체 길이가 3이 될 때까지 마지막 문자를 반복해서 붙였다.
작성한 코드는 다음과 같다.
def solution(new_id):
flags = [
"a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z",
"-", "_", "."
] + [str(x) for x in range(0, 10)]
# 1단계
new_id = new_id.lower()
# 2단계
for flag in set(list(new_id)):
if flag not in flags:
new_id = new_id.replace(flag, "")
# 3단계
point_cnt = 0
for flag in new_id:
if flag == ".":
point_cnt += 1
if point_cnt > 1:
new_id = new_id.replace("."*point_cnt, ".")
point_cnt = 0
# 4단계
if (len(new_id)) and (new_id[0] == "."):
new_id = new_id[1:]
if (len(new_id)) and (new_id[-1] == "."):
new_id = new_id[:-1]
# 5단계
if len(new_id) == 0:
new_id = "a"*3
elif len(new_id) >= 16:
new_id = new_id[:15]
if new_id[-1] == ".":
new_id = new_id[:-1]
elif len(new_id) <= 2:
new_id = new_id + new_id[-1] * (3-len(new_id))
return new_id이 코드는 문제에서 제시한 조건을 그대로 따라가면서 구현한 방식이다. 각 단계를 직접 나누어 처리했기 때문에 동작 흐름을 이해하기 쉽다는 장점이 있다. 다만 문자열 치환을 여러 번 수행하고, 직접 조건을 하나씩 처리하기 때문에 코드가 다소 길어지는 편이다.
다른 참여자의 풀이 중에서는 정규표현식을 사용한 답안이 가장 인상 깊었다. 정규표현식을 사용하면 각 단계에서 처리해야 하는 문자열 조건을 훨씬 간결하게 표현할 수 있다.
import re
def solution(new_id):
st = new_id
st = st.lower()
st = re.sub('[^a-z0-9\-_.]', '', st)
st = re.sub('[.]+', '.', st)
st = re.sub('^[.]|[.]$', '', st)
st = 'a' if len(st) == 0 else st[:15]
st = re.sub('^[.]|[.]$', '', st)
st = st if len(st) > 2 else st + "".join([st[-1] for i in range(3-len(st))])
return st정규표현식을 사용한 코드는 확실히 제가 작성한 코드보다 짧고 간결하다. 특히 사용할 수 없는 문자를 제거하는 부분이나, 연속된 마침표를 하나로 줄이는 부분은 정규표현식과 잘 어울린다.
st = re.sub('[^a-z0-9\-_.]', '', st)
st = re.sub('[.]+', '.', st)첫 번째 정규표현식은 소문자 알파벳, 숫자, 빼기, 밑줄, 마침표를 제외한 문자를 제거한다. 두 번째 정규표현식은 하나 이상 연속된 마침표를 하나의 마침표로 치환한다. 처음 풀이를 작성할 때는 문제의 7단계를 그대로 코드로 옮기는 데 집중했다. 그래서 각 단계가 명확하게 드러나는 코드를 작성할 수 있었다. 반면 정규표현식을 사용한 풀이는 같은 처리를 더 짧고 효율적으로 표현한다. 이번 문제는 단순히 정답을 맞히는 것뿐만 아니라, 문자열 처리 문제에서 정규표현식을 적절히 사용하면 코드가 얼마나 간결해질 수 있는지 확인할 수 있는 문제였다.