문제
카카오에 입사한 신입 개발자 네오는 "카카오계정개발팀"에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다. "네오"에게 주어진 첫 업무는 새로 가입하는 유저들이 카카오 아이디 규칙에 맞지 않는 아이디를 입력했을 때, 입력된 아이디와 유사하면서 규칙에 맞는 아이디를 추천해주는 프로그램을 개발하는 것입니다.
다음은 카카오 아이디의 규칙입니다.
- 아이디의 길이는 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단계를 거치고 나면 new_id는 아래와 같이 변경됩니다.
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" 입니다.
[문제]
신규 유저가 입력한 아이디를 나타내는 new_id가 매개변수로 주어질 때, "네오"가 설계한 7단계의 처리 과정을 거친 후의 추천 아이디를 return 하도록 solution 함수를 완성해 주세요.
[제한사항]
new_id는 길이 1 이상 1,000 이하인 문자열입니다.
new_id는 알파벳 대문자, 알파벳 소문자, 숫자, 특수문자로 구성되어 있습니다.
new_id에 나타날 수 있는 특수문자는 -_.~!@#$%^&*()=+[{]}:?,<>/ 로 한정됩니다.
예전 풀이
생각
5단계: 빈문자열
만약 new_id == []일 경우 append('a')
6단계: 15자로 만들기
만약 len(new_id) >= 16일 경우 [:15]
7단계: 3자로 만들기
만약 len(new_id) <= 2 일 경우 append(new_id[-1])
4단계: 맨 앞/뒤 '.'을 제거
따로 while문..? [0] != '.' and [-1] != '.' 인 동안
[0] == '.' 일 경우 del new_id[0], [len(new_id)-1] == '.'일 경우 del new_id[len(new_id)-1]
for문을 돌면서 문자 하나하나에 대한 변경이 필요한 것들 먼저 처리한다
1단계: 대문자를 소문자로
i.isupper() == True 이면 i.lower()
2단계: 주어진 문자 외에 모두 제거
1단계에서 모두 소문자로 바뀔 것이기 때문에 여기서 굳이 한 번 더 작업을 해줄 필요는 없을 것 같다
allow_list = ['-', '_', '.']
i not in allow_list or i.isdigit() == False 이면 i.pop() 아니면 i.remove()
3단계: .이 연속으로 나타날 경우 .으로 치환
맨 앞과 맨 뒤 .은 제거되었으므로 신경 쓰지 않는다
만약 [i] == '.'일 때 [i-1]=='.' 이면 del [i-1], [i+1]== '.'이면 del [i+1]
코드
def solution(new_id):
answer = ''
new_id = list(new_id)
# 5단계
if new_id == []:
new_id.append('a')
# 6단계
if len(new_id) >= 16:
new_id[:15]
# 7단계
if len(new_id) <= 2:
while len(new_id) >= 3:
new_id.append(new_id[-1])
# 4단계
while new_id[0] != '.' and new_id[-1] != '.':
if new_id[0] == '.':
del new_id[0]
elif new_id[-1] == '.':
del new_id[-1]
# 문자 하나하나에 대한 변경
for i in range(len(new_id)):
# 1단계
if new_id[i].isupper() == True:
new_id[i].lower()
# 2단계
allow_list = ['-', '_', '.']
if new_id[i] not in allow_list or new_id[i].isdigit() == False:
del new_id[i]
# 3단계
if new_id[i] == '.':
if new_id[i-1] == '.':
del new_id[i-1]
if new_id[i+1] == '.':
del new_id[i+1]
return answer
다시 생각해보기
생각
위와 비슷한 방법으로 할 것 같다
6단계에서 마지막 .을 지우라고 하는데 4단계를 지날 대 지워지니까 괜찮지 않을까 싶다
코드
1차 시도
def solution(new_id):
new_id = list(new_id)
# 5단계
if new_id == []:
new_id.append('a')
# 6단계
if len(new_id) >= 16:
new_id = new_id[:15]
# 7단계
if len(new_id) <= 2:
while len(new_id) == 3:
new_id.append(new_id[-1])
# 4단계
while True:
if new_id[0] == '.':
del new_id[0]
elif new_id[-1] == '.':
del new_id[-1]
else:
break
# 문자 하나하나에 대한 변경
for i in range(len(new_id)):
# 1단계
if new_id[i].isupper() == True:
new_id[i] = new_id[i].lower()
# 2단계
allow_list = ['-', '_', '.']
if new_id[i] not in allow_list or new_id[i].isdigit() == False:
del new_id[i]
# 3단계
if new_id[i] == '.':
if new_id[i-1] == '.':
del new_id[i-1]
return new_id
- 덮어쓰기 안 된 부분 고침
- 7단계 길이가 3이상을 3과 같을 때로 바꿈
- 4단계에서 while문에 그런 조건을 사용할 수 없다고 함. 그냥 True로 해놓고 else문을 추가해서 양 끝이 .이 아닌 경우에 대해 정의함
- new_id를 반환하지 않고 있었음. 아니면 answer에 할당이라도 해주던가...😶
- 근데 28번줄에서 IndexError: list index out of range에러남,, 보니까 i가 new_id의 길이보다 하나 더 돌면서 에러나던데 뭐가 잘못됐는지 모르겠다.
다른 사람 풀이
def solution(new_id):
# 1단계
new_id = new_id.lower()
# 2단계
answer = ''
for word in new_id:
if word.isalnum() or word in '-_.':
answer += word
# 3단계
while '..' in answer:
answer = answer.replace('..', '.')
# 4단계
answer = answer[1:] if answer[0] == '.' and len(answer) > 1 else answer
answer = answer[:-1] if answer[-1] == '.' else answer
# 5단계
answer = 'a' if answer == '' else answer
# 6단계
if len(answer) >= 16:
answer = answer[:15]
if answer[-1] == '.':
answer = answer[:-1]
# 7단계
if len(answer) <= 3:
answer = answer + answer[-1] * (3-len(answer))
return answer
코드 스스로 해석하고 이해하기
- 1단계에서 new_id 전체를 소문자로 바꾼다 (조건 검사 없이 이렇게 바꾸면 되는구나..!)
- 2단계에서 빈문자열 answer를 정의하고 new_id를 돌면서 만약 한 문자가 알파벳과 숫자로 이루어져 있는지, 아니면 -나_나. 인지 확인하여 이 조건에 만족하면 answer에 더해준다.
** isalnum() **
- 파이썬 문자열 메서드로, 문자열이 알파벳 문자(영문자)와 숫자로만 이루어져 있는지를 확인하는 기능
- 자열에 특수 문자, 공백 등이 포함되어 있지 않고 알파벳 문자나 숫자만 있을 때 True를 반환
- 3단계에서 ..이 answer안에 있다면 ..을 .으로 바꿔준다. (i와 i 옆자리를 비교할 거 없이 그냥 while문을 사용해서 ..이 들어있냐 아니냐만 보면 되는데 내 코드는 너무 빙빙 돈 것 같다)
- 4단계에서 만약 answer의 첫 번째 글자가 .이고 answer의 길이가 1 초과이면 answer의 두 번째 요소부터 끝까지 answer에 담고 아니라면 그냥 놔둔다. 그 다음 만약 answer의 마지막 글자가 .이면 answer의 첫 글자부터 마지막에서 두 번째 글자까지 answer에 담고 아니면 그냥 놔둔다.
- 5단계에서 answer가 빈 문자열이면 answer에 a를 담고 아니라면 그냥 놔둔다
- 6단계에서 만약 answer의 길이가 16이상이라면 answer의 첫 문자부터 15번째 문자까지만 answer에 저장하고, 이렇게 햇을 때 answer의 마지막 글자가 .이라면 마지막 글자를 제외한 글자들을 answer에 담아준다
- 7단계에서 만약 answer의 길이가 3 이하라면 (3 - answer길이) 숫자만큼 answer의 마지막 글자를 반복해 answer에 붙여준다.
def solution(new_id):
step1=[]
for i in new_id:
if ord('A')<=ord(i)<=ord('Z'):
step1.append(chr(ord(i)+32))
else:
step1.append(i)
step2=[]
for i in step1:
if ord('a')<=ord(i)<=ord('z') or i.isdigit()==True or i=="-" or i=="_" or i==".":
step2.append(i)
step3="".join(step2)
for i in range(1000):
if step3.find("..")!=-1:
step3=step3.replace("..",".")
else:
break
step4=[]
for i in range(len(step3)):
if step3[i]!=".":
step4.append(step3[i])
elif step3[i]==".":
if i==0 or i==len(step3)-1:
continue
else:
step4.append(step3[i])
if len(step4)==0:
return("a"*3)
step5=step4
while len(step5)>=16:
step5.pop()
if step5[-1]==".":
step5.pop()
step6=step5
while len(step6)<=2:
step6.append(step6[-1])
step7="".join(step6)
return(step7)
코드 스스로 해석하고 이해하기
- step1리스트를 만든다
- 입력된 new_id를 돌면서
- 만약 i가 유니코드 상 A와 Z 사이에 있다면 대문자이다. 그렇다면 i의 유니코드 번호에 32를 추가하고 해당 유니코드를 chr() 함수에 넣어 다시 문자로 반환. 이 문자를 step1리스트에 append 해준다.
** ord() **
- 주어진 문자의 유니코드 코드 포인트를 정수로 반환
- 주로 문자와 해당 문자의 유니코드 값을 매핑할 때 사용
** chr() **
- 유니코드 코드 포인트를 문자로 변환
- 만약 대문자가 아니라면 그냥 step1에 넣어준다
- step2 빈 리스트를 새로 생성해서
- step1 리스트를 돌며 만약 해당 문자가 소문자이거나, 숫자이거나, -이거나 _이거나 .이면 step2에 넣어준다
- step3문자열에는 step2를 붙여주고
- 1000범위로 for문을 돌면서 (new_id는 길이 1 이상 1,000 이하인 문자열입니다.조건 때문인듯)
- step3 내에 ..이 있는지 확인하고, 찾으면 step3 내에 있는 ..을 .으로 바꿔준 뒤 덮어쓰기 한다.
- step3 내에 없다면 for문을 나간다
** find() **
- 파이썬의 문자열 메서드로, 문자열 내에서 특정 부분 문자열의 첫 번째 발생 위치를 찾아 정수로 반환
- 부분 문자열이 찾아지지 않으면 -1을 반환
- 기본 형식: string.find(substring, start, end)
- substring: 찾고자 하는 부분 문자열
- start(선택): 검색을 시작할 위치 (기본값은 0)
- end(선택): 검색을 끝낼 위치. 이 위치의 문자는 포함되지 않음 (기본값은 문자열의 끝)
- step4 빈문자열을 만들고
- step3 길이만큼 for문을 돌면서
- 만약 문자가 .이 아니라면 step4에 넣어준다
- 만약 문자가 .이라면 해당 문자의 인덱스를 확인한다. 인덱스가 0이나 마지막 인덱스라면 step4에 추가하지 않은 채로 continue하고 인덱스가 맨 끝이 아니라면 step4에 넣어준다
- 만약 step4가 길이가 0이면 즉 빈문자열이면 a를 세 번 리턴해준다. (그럼 다른 과정을 거치지 않고 답을 aaa로 내겠다는건가?)
- step4를 step5변수에 넣고
- step5의 길이가 16이상인 동안 step5의 요소들을 지워준다. pop()에 아무 인자를 전달하지 않으면 맨 뒤 요소를 제거하는거다
- step5의 결과를 step6에 넣는다.
- step6의 길이가 2 이하인 동안 step6에 step6의 마지막 문자를 넣어준다
- step7의 빈 문자열에 step6를 넣고 (즉 step6를 문자열로 만들고) step7를 반환한다.
뭔가 가독성은 위 코드가 더 좋긴 한데 밑에 있는 코드는 다양한 함수를 활용해서 두 코드의 시간복잡도가 어떨지 비교해보고 싶다.
chatGPT한테 물어봤는데

그렇다고 한다!
'Python > 프로그래머스' 카테고리의 다른 글
[프로그래머스 lv.1]성격 유형 검사하기(건너뛴 문제 다시풀기) (1) | 2023.09.25 |
---|---|
[프로그래머스 lv.1]최소직사각형(건너뛴 문제 다시풀기) (0) | 2023.09.20 |
[프로그래머스 lv.1] 3진법 뒤집기 (건너뛴 문제 다시풀기) (0) | 2023.09.18 |
[프로그래머스 lv.1] [카카오 인턴] 키패드 누르기 (건너뛴 문제 다시풀기) (0) | 2023.09.17 |
[프로그래머스 lv.1] 크레인 인형뽑기 게임 (건너뛴 문제 다시풀기) (0) | 2023.09.17 |