본문 바로가기

알고리즘풀이/프로그래머스

[12/5] Lv1.

👉 사용 언어 : PYTHON3

신규 아이디 추천

✅ Solution

def solution(new_id):
    s_ch = '~!@#$%^&*()=+[{]}:?,<>/'
    ans = list(new_id.lower())
    for i in ans:
        if i in s_ch:
            ans[ans.index(i)] = ' '
    ans = [ch for ch in ans if ch != ' ']
    for i in range(len(ans)-1):
        if ans[i] == '.' and ans[i+1] == '.':
            ans[i] = ' '
    str_ans = (''.join(ans)).replace(' ', '')
    for j in range(len(str_ans)):
        if str_ans[j] == '.':
            str_ans = str_ans.replace('.', ' ', 1)
        else:
            break
    str_ans = str_ans.replace(' ', '')
    if len(str_ans) == 0:
        str_ans += 'a'
    str_ans = str_ans[0:15]
    for j in range(len(str_ans)-1, -1, -1):
        if str_ans[j] != '.':
            str_ans = str_ans[0:j+1]
            break
    if len(str_ans) <= 2:
        str_ans += (str_ans[len(str_ans)-1]*(3-len(str_ans)))
    return str_ans

 

👉 아이디어

  • 확인 단계별로 코드 작성
  • 고려해야할 경우의 수가 너무 많아져서 코드가 너무 더러워짐 ..
  • 정규표현식 사용하는 방향으로 다시 생각하기 ( 아래 다른 사람 풀이 참고해서 )

👉 문자열에서 .index( ) 를 사용하여 해당 값의 인덱스를 추출할 때, 중복 되는 값이 있으면 맨 처음 에 오는 값의 인덱스를 리턴한다.

     따라서, '...a'에서 .index('.') 를 하면 몇 번을 추출해도 0만 나온다.

👉 .startwith( ) 및 .endwith( ) : ~로 시작하는지? ~로 끝나는지? 판단

 

 다른 사람 풀이 참고

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

 

👉 정규표현식을 사용하려면 "re" 모듈 사용

👉 re.sub('[^a-z0-9\-_.]', '', st) : 소문자(a-z) 또는 숫자(0-9) 또는 (-_.)가 아닌 문자 제거 (''로)

👉 re.sub('\.+', '.', st) : '.' 이 여러 개 있으면 '.' 하나로 변환 (+ : + 앞에 있는 문자가 최소 한 번 이상 반복되어야 매치)

👉 re.sub('^[.]|[.]$', '', st) : '.'이 처음 또는 끝에 있으면 제거 (''로)

++ 빈 문자열일 경우 'a' 추가하는 조건과 문자열이 16자 이상일 경우 15자까지만 슬라이싱하는 조건을 동시에 구현 ++

 

🔗 정규표현식 참고 사이트 : https://nachwon.github.io/regular-expressions/

 

[Python 문법] 정규표현식 (Regular Expressions)

정규표현식에 대해 알아보고 Python으로 정규표현식을 사용하는 방법에 대해 알아본다.

nachwon.github.io

 

로또의 최고 순위와 최저 순위

 

✅ Solution

def rank(score):
    if score > 1:
        return 7-score
    else:
        return 6
    
def solution(lottos, win_nums):
    answer = 0
    cnt_0 = lottos.count(0)
    for i in lottos:
        if i in win_nums:
            answer += 1
    return [rank(answer+cnt_0), rank(answer)]

 

👉 아이디어

  • lottos 배열을 반복문을 돌려 순회한다.
    • 원소 in win_nums 해서 true 이면 카운트한다.
    • .count( ) 사용하여 0의 개수를 카운트한다.
  • min : true일때 카운트한 값 , max : min + 0일때 카운트한 값
  • 등수 = 7-점수 (대칭)이며 점수가 0, 1일때 모두 6등이 된다는 것만 예외 로 처리해주면된다.
    • 점수 > 1 : 7-점수(=등수) 리턴
    • 점수 <= 1 : 6 (=등수) 리턴
  • [max 등수 , min 등수]를 리턴한다.

 다른 사람 풀이 참고

def solution(lottos, win_nums):

    rank=[6,6,5,4,3,2,1]

    cnt_0 = lottos.count(0)
    ans = 0
    for x in win_nums:
        if x in lottos:
            ans += 1
    return rank[cnt_0 + ans],rank[ans]

 

👉 비슷한 아이디어인데 아예 인덱스(=점수)와 원소(=등수)를 사용하여 더 깔끔하게 풀이하였다.

👉 []로 따로 안묶어주고 그냥 값을 2개 이상 리턴하면 자동으로 리스트로 묶어준다 !!

개인정보 수집 유효기간

✅ Solution

from datetime import datetime
def solution(today, terms, privacies):
    ans = []
    term_exp = {}
    exp_date = []
    today_date = datetime.strptime(today, "%Y.%m.%d")
    for i in terms:
        term_exp[i.split()[0]] = i.split()[1]
    for j in privacies:
        date = j.split()[0]
        month = int(date.split('.')[1]) + int(term_exp[j.split()[1]])
        year = int(date.split('.')[0])
        if month <= 12:
            exp_date.append(date.split('.')[0]+'.'+str(month)+'.'+date.split('.')[2])
        elif month > 12 and month % 12 != 0:
            exp_date.append(str(year+month//12)+'.'+str(month-12*(month//12))+'.'+date.split('.')[2])
        else:
            exp_date.append(str(year+(month//12-1))+'.'+'12'+'.'+date.split('.')[2])
    for k in exp_date:
        k_date = datetime.strptime(k, "%Y.%m.%d")
        if k_date <= today_date:
            ans.append(exp_date.index(k)+1)
            exp_date[exp_date.index(k)] = ''
    return ans

 

👉 아이디어

  • "약관-유효기간" 딕셔너리 생성 (term_exp) _ 약관 종류 중복되지 않으므로 key로 사용 가능함
  • "파기 날짜" 배열 생성 (exp_date)
  • 약관 유효기간 + 현재 달 통해 파기 날짜 구할 때, 달이 12로 나누어떨어지는 경우 처리 필요 _ 0이 될 수 있음
    • 약관 유효기간 + 현재 달 <= 12인 경우
    • 약관 유효기간 + 현재 달 > 12이면서 12의 배수가 아닌 경우
    • 약관 유효기간 + 현재 달 > 12이면서 12의 배수인 경우 ✔️
  • today 날짜와 exp_date의 각 날짜를 비교하여 후자가 더 작거나 같은 경우 정답 배열에 인덱스+1 추가
    • .index( ) 사용할때는 무조건 중복 원소에서 실패 원인이 있을 경우가 대부분! ( 맨 처음 원소 인덱스만 리턴하므로 )
    • 해당 풀이에서는 정답 배열에 추가한 값은 ''로 초기화시켜 중복 문제를 해결하였다.

 다른 사람 풀이 참고

def to_days(date):
    year, month, day = map(int, date.split("."))
    return year * 28 * 12 + month * 28 + day

def solution(today, terms, privacies):
    months = {v[0]: int(v[2:]) * 28 for v in terms}
    today = to_days(today)
    expire = [
        i + 1 for i, privacy in enumerate(privacies)
        if to_days(privacy[:-2]) + months[privacy[-1]] <= today
    ]
    return expire

 

👉 "map"을 사용하여 년도 / 월 / 일 3가지 변수에 한번에 저장

👉 날짜를 문자열 or datetime 형식으로 비교하지 않고 년/월을 모두 *28 하여 day로 변환 하여 그 값(정수)끼리 비교하였다.

👉 months : "약관-유효기간" 딕셔너리

👉 "enumerate" 사용하여 privacy 정보("개인정보 수집 일자 및 약관 종류")와 순서 정보(index+1)을 동시에 접근 가능하도록 하였다.

둘만의 암호

✅ Solution

def solution(s, skip, index):
    answer = ''
    for i in s:
        cnt = 1
        base = ord(i) + cnt
        while cnt <= index:
            if base > ord('z'):
                base -= 26
            if chr(base) not in skip:
                cnt += 1
            base += 1
        answer += chr(base-1)
    return answer

 

👉 아이디어

  • 에 각각 index 만큼 더한 문자를 다시 저장
    • 아스키코드 사용 ( "a" = 97 ~ "z" = 122 )
    • 문자 > 아스키코드 변환 : ord()
    • 이 과정에서 skip 에 있는 문자는 제외해야 함
  • s 의 원소중 skip 에 있는 문자가 존재하면 해당 원소 + (index-s의 원래 문자)

👉 문자열.find("특정 문자")

    : 문자열 내 특정 문자 찾는 함수

    : 있으면 해당 문자의 index를, 없으면 -1을 리턴한다.

👉 특정 문자 in 문자열 

    : 문자열 내 특정 문자 찾는 함수

    : 있으면 true를, 없으면 false를 리턴한다.

    : 반대 _ 특정 문자 not in 문자열

 

 다른 사람 풀이 참고

from string import ascii_lowercase

def solution(s, skip, index):
    result = ''

    a_to_z = set(ascii_lowercase)
    a_to_z -= set(skip)
    a_to_z = sorted(a_to_z)
    l = len(a_to_z)

    dic_alpha = {alpha:idx for idx, alpha in enumerate(a_to_z)}

    for i in s:
        result += a_to_z[(dic_alpha[i] + index) % l]

    return result

 

햄버거 만들기

✅ Solution

def solution(ingredient):
    answer = ''.join(map(str, ingredient))
    while "1231" in answer:
        answer = answer.replace("1231", "", 1)
    return (len(ingredient) - len(answer)) // 4

 

👉 주의 _ "1231"을 한번에 여러 개 제거할 수 없다.

    ( 그래서 위의 코드처럼 answer.replace("1231", "", 1)을 썼더니 시간 초과 )

👉 replace 를 쓰면 결국 문자열의 전체 길이를 탐색 + @ 해서 시간복잡도가 꽤 걸린다.

 

def solution(ingredient):
    i = 0
    ans = 0
    while i < len(ingredient)-3:
        if ingredient[i:i+4] == [1, 2, 3, 1]:
            del ingredient[i:i+4]
            ans += 1
            if i < 4:
                i = 0
            else:
                i -= 3
        else:
            i += 1
    return ans

 

👉 [1, 2, 3, 1]을 del을 사용하여 지운 후, 두 개의 케이스로 나눠 i를 초기화한다.

  • i가 4보다 작을 경우 : 아예 0으로 초기화
  • i가 4보다 크거나 같을 경우 : 지운 값은 [i] ~ [i+3]이므로 다시 [1, 2, 3, 1]이 나올 수 있는 범위는 i-3부터이므로 i-3으로 초기화
  • 이렇게 나눠서 값을 초기화하면 시간초과를 방지할 수 있다.

👉 아이디어

  • 정수배열 ingredient를 문자열로 만들기
    • ''.join(map(str, ingredient))
  • 해당 문자열에서 "1231"이 없을때까지 반복문 돌려서 .replace("1234", "")하기
  • (원래 ingredient 길이 - 남은 문자열 길이) // 4 를 리턴한다. 

👉 리스트(변형 객체) 복사

  • 리스트1 = 리스트2  : C 언어에서 포인터만 지정한 것과 비슷한 경우 (list는 1개만 존재하지만 2가지 이름으로 가리키는 것)
    • 리스트1을 수정하면 리스트2에도 반영되고, 리스트2를 수정하면 리스트1에도 반영되는 이슈 발생
  • shallow copy(얕은 복사 ) : 하나의 리스트를 수정해도 다른 하나가 바뀌지 않는다.
    • 리스트1 = 리스트2.copy()
    • 리스트1 = 리스트2[ : ]

 

 다른 사람 풀이 참고

def solution(ingredient):
    s = []
    cnt = 0
    for i in ingredient:
        s.append(i)
        if s[-4:] == [1, 2, 3, 1]:
            cnt += 1
            for i in range(4):
                s.pop()
    return cnt

 

👉 시간복잡도가 훨씬 적음

👉 새로운 배열에 값을 하나씩 넣고 해당 배열에서 [1, 2, 3, 1]이 존재하면 카운트+1

    ( 그리고나서 새로운 배열에서 [1, 2, 3, 1]을 pop( ) 한다. )

'알고리즘풀이 > 프로그래머스' 카테고리의 다른 글

[1/25] Kakao Internship 기출문제 풀이  (0) 2024.01.18
[1/18] Kakao Internship 기출문제 풀이  (0) 2024.01.11
[성격 유형 검사하기]  (2) 2023.11.28
[11/22] Lv1.  (2) 2023.11.18
[11/16] Lv1.  (0) 2023.11.10