문자열 다룰 때 필수적으로 갖춰야할 스킬들
1. 문제
https://programmers.co.kr/learn/courses/30/lessons/72410
카카오에 입사한 신입 개발자 네오는 “카카오계정개발팀”에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다.
“네오”에게 주어진 첫 업무는 새로 가입하는 유저들이 카카오 아이디 규칙에 맞지 않는 아이디를 입력했을 때, 입력된 아이디와 유사하면서 규칙에 맞는 아이디를 추천해주는 프로그램을 개발하는 것입니다.
다음은 카카오 아이디의 규칙입니다.
“네오”는 다음과 같이 7단계의 순차적인 처리 과정을 통해 신규 유저가 입력한 아이디가 카카오 아이디 규칙에 맞는 지 검사하고 규칙에 맞지 않은 경우 규칙에 맞는 새로운 아이디를 추천해 주려고 합니다.
신규 유저가 입력한 아이디를 나타내는 new_id가 매개변수로 주어질 때, “네오”가 설계한 7단계의 처리 과정을 거친 후의 추천 아이디를 return하도록 solution 함수를 완성하세요
2. 제한사항
new_id는 길이 1 이상 1000이하인 문자열
new_id는 알파벳 대문자, 소문자, 숫자, 특수문자로 구성
new_id에 나타날 수 있는 특수문자는 -_.~!@#$%^&*()=+[{]}:?,<>/
3. 입출력 예시
4. 나의 풀이
1,2,3,4,5,6,7 단계 알고리즘에 맞게 순차적으로 코딩하면 된다
특수문자는 마침표(.), 빼기(-), 밑줄(_)만 필요하므로 리스트에 따로 저장
1단계. 모든 대문자를 소문자로 치환
대문자를 소문자로 바꾸는 method가 lower()
def solution(new_id):
special_list = ['.', '-', '_']
#one
one_id = new_id.lower()
1단계를 거친 문자열이 one_id
2단계. 알파벳 소문자, 숫자, 빼기, 밑줄, 마침표를 제외한 모든 문자 제거
다음 대문자를 소문자로 바꾼 문자열 one_id에서 문자를 하나씩 빼서 알파벳 소문자인지 숫자인지 빼기, 밑줄, 마침표인지 검사함
알파벳은 소문자로 바꿨으므로 알파벳인지만 검사하는 isalpha()
숫자는 isdigit(),
빼기, 밑줄, 마침표에 해당하는지는 special_list에 저장해둔 리스트를 이용해서 char in special list
before_char = ''
for index,char in enumerate(one_id):
if (char.isalpha()) or (char.isdigit()) or (char in special_list):
이 조건문을 통과하지 못하면 자연스럽게 제거되는거고 통과한다면
3단계. 마침표가 2번 이상 연속된 부분을 하나의 마침표로 치환
마침표가 연속되는걸 제거하기 위해서 before_char이라는걸 도입함
enumerate로 현재 index가 어디인지를 표시했는데 index=0이면
이전 char이랑은 비교할 필요는 없으므로 바로 id_list에 넣어주고 현재 char을 before_char로 한다
if index == 0:
id_list.append(char)
before_char = char
다음 index부터는 마침표가 연속될 수 있는데 before_char이 마침표인 경우와 아닌 경우로 나뉘어서
before_char이 마침표가 아닌 경우에는 그냥 id_list에 넣어주고
before_char이 마침표인 경우에는
현재 char이 마침표이면 마침표가 연속되고있는 경우이므로 현재 char을 id_list에 넣지 않는다
반대로 현재 char이 마침표가 아니면 연속되고 있는 경우가 아니므로 현재 char을 id_list에 넣어준다
else:
if before_char == '.':
if char == '.':
before_char = char
else:
id_list.append(char)
before_char = char
else:
id_list.append(char)
before_char = char
이제 5,6,7단계를 해결해야하는데 이 과정을 통과하고 나면 id_list 길이가 0일 수 있다..
처음부터 알파벳 소문자, 숫자, 빼기, 밑줄, 마침표로 구성된 문자열이 아니라면 조건에 맞는 문자가 없었으므로 id_list에 아무것도 안들어와
5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입
7단계 new_id의 길이가 2자 이하라면 new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙임
을 조합하면.. id_list의 길이가 0이면 'aaa'를 return함
if len(id_list) == 0:
return 'aaa'
반대로 만약 id_list에 하나라도 문자가 들어왔다면 4단계 new_id에서 마침표가 처음이나 끝에 위치한다면 제거를 처리해야하는데
마침표가 처음이나 끝에 위치하면 제거해야하므로 id_list[0]과 id_list[-1]을 이용한다
id_list[0]이 마침표라면 제거하는데 제거하고나서 id_list의 길이가 0일 수 있다.. 그 경우 5단계 이후 7단계로 넘어가서 ‘aaa’를 바로 return
else:
#four
if id_list[0] == '.':
del id_list[0]
if len(id_list) == 0:
return 'aaa'
길이가 0이 아니라면 끝에 있는 마침표도 제거할 수 있으므로 id_list[-1]이 마침표인지 검사하고 마침표라면 제거
제거하고 나서 길이가 0이면 ‘aaa’를 바로 return
if id_list[-1] == '.':
del id_list[-1]
if len(id_list) == 0:
return 'aaa'
다음으로 6단계 new_id의 길이가 16자 이상이면 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거하고 마침표가 끝에 위치한다면 그 마침표를 제거
id_list의 길이가 16이상이라면 첫 15개의 문자만 가지고와야하므로 id_list[:15]를 새로운 new_id_list로 하고
여기에도 끝에 마침표라면, new_id_list[-1]이 마침표라면 제거하고나서 ‘’.join(new_id_list)를 통해 문자열로 만들고 return
리스트를 문자열로 만들 때는 ''.join(<리스트>)
if len(id_list) >= 16:
new_id_list = id_list[:15]
if new_id_list[-1] == '.':
del new_id_list[-1]
return ''.join(new_id_list)
그런데 6단계까지 통과하고 나서 7단계. new_id의 길이가 2자 이하이면 new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복을 처리해야함
id_list의 길이가 2이하라면 현재 id_list에서 마지막 문자를 길이가 3이 될 때까지 반복해야하므로
if len(id_list) <= 2:
last_char = id_list[-1]
while 1:
id_list.append(last_char)
if len(id_list) == 3:
break
return ''.join(id_list)
마지막 문자를 id_list[-1]을 따로 저장한 뒤에 while 1:을 이용해서 마지막 문자를 id_list에 append하는 작업을 반복함
길이가 3이 되면 break를 이용해 반복문을 탈출하고 ‘’.join(id_list)로 문자열로 만들고 return
그런데 4단계를 끝내고나서 id_list의 길이가 0도 아니고 2이하도 아니고 16이상도 아닌경우가 있을 수 있는데
그런경우에는 그냥 바로 5,6,7단계를 처리하지않고(조건에 안맞으니까) ‘’.join(id_list)로 문자열로 바꿔서 return
return ''.join(id_list)
5. 다른 풀이
연속되는 마침표를 제거하는 방법을 조금 더 깔끔하게 할 수가 있는데
id_list = []
for char in one_id:
if char.isalnum() or (char in special_list):
id_list.append(char)
two_id = ''.join(id_list)
while '..' in two_id:
two_id = two_id.replace('..','.')
isalpha()와 isdigit()은 isalnum()을 이용해서 하나로 바꿀 수 있다
isalpha()나 isalnum()은 알파벳뿐만 아니라 한글이어도 True를 return한다고함
소문자, 숫자, 빼기, 마침표, 밑줄 조건에 맞는 문자만 가지고와서 문자열로 바꾼다음에
그리고 while문을 이용해서 ‘..’이 two_id에 존재할때까지 two_id에서 ‘..’을 ‘.’으로 replace하는 작업을 반복적으로 수행함
당연히 마침표가 ..만 있는게 아니라 ........ 여러개 있을 수 있음
하지만 ........이 여러개더라도 결국엔 반복문을 통해 ‘...’이 ‘..’이 되고 ‘.’으로 되므로 while문이 끝나면
연속하는 마침표는 결국에는 하나의 마침표로만 바뀜
혹은 정규표현식으로도 풀수 있는데 이건 외우는게 아니고 찾아보는거라 약간 애매함
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
6. 되돌아보기
논리적으로 생각 잘해서 코딩 잘했다.
문자 하나라도 제거하면 길이가 0이 될 수 있으니까 이것도 고려해야한다는 점을 결국에는 생각했다는거 칭찬할만하고
5단계와 7단계를 조합해서 id_list의 길이가 0이면 바로 ‘aaa’를 return해서 시간 단축에 신경쓰고 있다는 점 칭찬할만하고
인덱스 지정할 때 첫번째 문자를 id_list[0], 마지막 문자를 id_list[-1] + 15개의 문자만 가지고 오는걸 id_list[:15]로 깔끔하게 했다는거
isalpha와 isdigit을 isalnum으로 합칠수 있다는것 + 한글도 검사함
특수문자 검사할때 필요한 특수문자를 리스트에 저장한 다음에 char in <list> 이런식으로 한다는 것도
가장 하이라이트는 사실 연속되는 마침표를 제거하는데 while ‘..’ in two_id: two_id = two_id.replace(‘..’,’.’)으로 할 수 있다는거 반드시 기억
마침표가 2개가 아니고 ........... 이더라도 반복문을 통해 .이 하나씩 줄어들면서 결국엔 . 하나만 남게된다는 원리
정규표현식이 문자열 replace하는데 쓴다는거 기억하고 있으면 정규표현식 연습장에서 필요할때 찾아보면 되니까.. 기억만 하고 있는 정도로
'알고리즘 > 알고리즘 일반' 카테고리의 다른 글
시간 다루기 (0) | 2021.11.22 |
---|---|
체스판에서 정사각형의 개수 (0) | 2021.11.22 |
완전히 탐색해야할때는.. (0) | 2021.11.21 |
반복문을 줄이는 방법 (0) | 2021.11.19 |
stack 활용법 - 올바른 괄호 문자열 판별 (0) | 2021.11.17 |