https://school.programmers.co.kr/learn/courses/30/lessons/86051

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def solution(numbers): 
    check = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    temp = set(check) - set(numbers)

    return sum(temp)

https://school.programmers.co.kr/learn/courses/30/lessons/136798

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def get_len_divisor(n):
    divisorsList = []

    for i in range(1, int(n**(1/2)) + 1):
        if (n % i == 0):
            divisorsList.append(i) 
            if ((i**2) != n) : 
                divisorsList.append(n // i)
    return len(divisorsList)

def solution(number, limit, power):
    answer = 0
    counts = []
    
    for i in range(1, number + 1):
        counts.append(get_len_divisor(i))
    
    for i, c in enumerate(counts):
        if c > limit:
            counts[i] = power
        else:
            pass
     
    return sum(counts)

 

와우 드디어 비기너 탈출

https://school.programmers.co.kr/learn/courses/30/lessons/159994

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def solution(cards1, cards2, goal):
    answer = "Yes"

    for g in goal:  
        if len(cards1) > 0 and g == cards1[0]: 
            cards1.pop(0)
        elif len(cards2) > 0 and g == cards2[0]:
            cards2.pop(0) 
        else:
            answer = "No"
            break 

    return answer

 

1. 입력과 목표의 관계 파악

  • cards1과 cards2는 각 단어가 순서대로 배열되어 있습니다.
  • goal을 순서대로 탐색하면서, 해당 단어가 cards1 또는 cards2의 맨 앞에 있는지 확인해야 합니다.

2. 구현 로직

  1. goal 배열을 순회
    • goal의 각 단어를 하나씩 확인합니다.
    • 현재 단어(current_word)를 가져옵니다.
  2. cards1과 cards2의 첫 번째 단어 비교
    • cards1의 맨 앞 단어가 current_word와 같다면:
      • cards1의 첫 번째 단어를 제거합니다.
    • cards2의 맨 앞 단어가 current_word와 같다면:
      • cards2의 첫 번째 단어를 제거합니다.
    • 둘 다 아니면:
      • goal을 만들 수 없으므로 "No"를 반환합니다.
  3. 모든 단어를 확인
    • goal 배열을 끝까지 순회했다면 "Yes"를 반환합니다.

3. 힌트: 카드 제거

  • cards1과 cards2에서 맨 앞의 단어를 확인하고 제거하는 동작이 필요합니다.
  • 이를 위해 리스트의 첫 번째 요소를 읽고 제거하는 방식을 사용합니다.

4. 조건별 흐름 정리

단어 탐색:

  • current_word가 cards1[0]과 같으면:
    • cards1의 첫 번째 단어를 제거합니다.
  • 아니고 current_word가 cards2[0]과 같으면:
    • cards2의 첫 번째 단어를 제거합니다.
  • 둘 다 아니면:
    • "No"를 반환합니다.

반복 종료:

  • goal의 모든 단어를 처리했다면:
    • "Yes"를 반환합니다.

5. 반복문으로 처리

  • goal 배열을 하나씩 확인하고 처리합니다.
  • 카드 뭉치의 맨 앞 단어와 비교하며, 일치하면 제거하고 다음 단어로 이동합니다.

6. 예제 분석

예제 1:

  • cards1: ["i", "drink", "water"]
  • cards2: ["want", "to"]
  • goal: ["i", "want", "to", "drink", "water"]
  1. goal[0] = "i" → cards1에서 제거.
  2. goal[1] = "want" → cards2에서 제거.
  3. goal[2] = "to" → cards2에서 제거.
  4. goal[3] = "drink" → cards1에서 제거.
  5. goal[4] = "water" → cards1에서 제거.
  6. 모두 처리 → "Yes".

7. 구현 시 유의할 점

  • cards1과 cards2는 비어있을 수도 있으니 비교 전에 확인하세요.
  • goal을 모두 처리하면 "Yes"를 반환하세요.

*

```

if len(cards1) == 0 or len(cards2) == 0: continue

```

 

현재 코드에서 발생하는 문제는 cards1이나 cards2 중 하나가 비어있을 때 goal의 단어를 건너뛰는 것입니다. 이로 인해 원하는 로직이 제대로 실행되지 않습니다. 이 문제를 해결하기 위해 아래 내용을 고려해 보세요:


1. 조건 순서의 문제

  • if len(cards1) == 0 or len(cards2) == 0: 부분이 모든 경우를 건너뛰게 만듭니다.
  • 사실 cards1 또는 cards2가 비어 있어도, 남은 단어들이 여전히 다른 리스트에 있는지 확인해야 합니다.
  • 이 조건을 각 리스트가 비어 있는 경우에도 처리하도록 수정해야 합니다.

2. 리스트가 비어 있을 때 조건 추가

  • cards1 또는 cards2가 비어 있을 수 있습니다. 따라서 각각의 조건을 따로 처리해야 합니다.
  • 예를 들어:
    • cards1이 비어 있는 경우: cards2[0]만 비교.
    • cards2가 비어 있는 경우: cards1[0]만 비교.

3. 수정할 조건의 흐름

  1. goal의 현재 단어(g)가 cards1[0]과 같다면:
    • cards1의 첫 번째 단어를 제거합니다.
  2. goal의 현재 단어가 cards2[0]과 같다면:
    • cards2의 첫 번째 단어를 제거합니다.
  3. 두 리스트가 모두 비어 있을 경우를 따로 건너뛰지는 않도록 해야 합니다.

4. 중요한 점

  • cards1.remove(g) 대신 pop(0)을 사용하세요.
    • remove는 리스트에서 해당 값을 찾아 제거하는 데 시간 복잡도가 O(n)이라 비효율적입니다.
    • pop(0)은 첫 번째 값을 제거하므로 문제의 요구사항에 맞고, 논리적으로도 더 깔끔합니다.
  • cards1이나 cards2가 비어 있는 경우에도 오류가 나지 않도록 각각의 조건을 개별적으로 처리해야 합니다.
 

지금 코드에서 발생하는 문제는 answer의 값을 "No"로 설정한 뒤에도 반복문이 계속 진행되고, 마지막에 answer = "Yes"로 덮어쓰여지는 데에 있습니다. 이로 인해 원하는 조건이 충족되지 않았음에도 불구하고 "Yes"가 반환됩니다.


문제의 원인

  • else 블록에서 answer = "No"로 설정했지만, continue를 사용하면서도 루프가 끝날 때 마지막에 answer = "Yes"가 실행됩니다.
  • 반복문이 끝날 때까지 모든 조건을 만족했는지 확인하지 않고, 무조건 "Yes"로 설정하기 때문에 잘못된 결과가 나옵니다.

해결 방법

  1. 반복문을 즉시 종료
    • goal 배열을 처리하다가 조건을 만족하지 않는 경우, 더 이상 확인할 필요가 없으므로 즉시 "No"를 반환해야 합니다.
    • continue가 아닌, return "No"를 사용하면 문제를 해결할 수 있습니다.
  2. answer 변수를 제거
    • 불필요하게 answer를 계속 업데이트할 필요가 없습니다.
    • 조건이 만족되지 않는 경우 바로 "No"를 반환하고, 반복문을 끝까지 수행했다면 "Yes"를 반환하면 됩니다.

수정된 코드 흐름

  1. goal의 현재 단어(g)가:
    • cards1의 첫 번째 단어와 같으면 제거.
    • cards2의 첫 번째 단어와 같으면 제거.
  2. 두 리스트의 첫 번째 단어와도 같지 않으면 즉시 "No"를 반환.
  3. 반복문을 끝까지 수행하면 "Yes"를 반환.

 

https://school.programmers.co.kr/learn/courses/30/lessons/17682

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def solution(dartResult):
    answer = 0
    temp = []
    opts = []
    cnt = 0  # 현재 몇 번째 점수인지 추적

    # Step 1: 문자열 파싱
    for i in dartResult:
        if i.isalpha():
            a = dartResult[:((dartResult.index(i)) + 1)]
            temp.append(a)
            dartResult = dartResult.replace(a, '', 1)  # 첫 번째 등장만 제거
            cnt += 1
        elif i in '#*':
            dartResult = dartResult.replace(i, '', 1)  # 첫 번째 등장만 제거
            opts.append((i, cnt - 1))  # 옵션과 대상 점수의 인덱스를 저장

    # Step 2: 점수 계산
    calcul = []
    for value in temp:
        if len(value) > 1:
            nums, bonus = int(value[:-1]), value[-1]
            if bonus == 'S':
                nums = nums ** 1
            elif bonus == 'D':
                nums = nums ** 2
            elif bonus == 'T':
                nums = nums ** 3
            calcul.append(nums)

    # Step 3: 옵션 처리
    for opt, idx in opts:
        if opt == '*':
            calcul[idx] *= 2
            if idx > 0:  # 이전 점수도 2배
                calcul[idx - 1] *= 2
        elif opt == '#':
            calcul[idx] *= -1

    # Step 4: 최종 합산
    answer = sum(calcul)
    return answer

 

 

세상에 나쁜 코드는 없다.... 

 

 

< 다른 풀이 법> 

 

def solution(dartResult):
    import re
    
    # Step 1: 정규식으로 점수|보너스|[옵션] 패턴을 추출
    pattern = r"(\d+)([SDT])([*#]?)"
    matches = re.findall(pattern, dartResult)

    # Step 2: 계산 로직 구현
    scores = []
    for i, (num, bonus, option) in enumerate(matches):
        # 점수 계산
        num = int(num)
        if bonus == 'S':
            num **= 1
        elif bonus == 'D':
            num **= 2
        elif bonus == 'T':
            num **= 3

        # 옵션 처리
        if option == '*':
            num *= 2
            if i > 0:  # 이전 점수가 있다면 그것도 2배
                scores[i-1] *= 2
        elif option == '#':
            num *= -1

        scores.append(num)

    # 총점 계산
    return sum(scores)

 

정규식 패턴 설명

pattern = r"(\d+)([SDT])([*#]?)"
  1. \d+:
    • \d는 숫자(0~9)를 의미합니다.
    • +는 숫자가 1개 이상 연속으로 나타날 수 있음을 의미합니다.
    • 예: "10", "5", "1" 등 숫자로 된 점수를 추출합니다.
  2. ([SDT]):
    • 대괄호 [ ] 안에 S, D, T를 넣어 S, D, T 중 하나를 찾습니다.
    • 각 문자는 Single(S), Double(D), Triple(T)을 의미하며, 점수를 몇 제곱할지 결정합니다.
    • 괄호 ( )는 이 부분을 그룹화하여 나중에 결과로 추출하게 합니다.
  3. ([*#]?):
    • 대괄호 [ ] 안에 *, #를 넣어 이들 중 하나를 찾습니다.
    • ?는 해당 부분이 0개 또는 1개 있을 수 있음을 의미합니다.
    • 즉, 옵션이 없을 수도 있고, *나 # 중 하나가 있을 수도 있습니다.
    • 괄호 ( )는 이 부분을 그룹화하여 나중에 결과로 추출하게 합니다.

re.findall 설명

python
코드 복사
matches = re.findall(pattern, dartResult)
  • re.findall 함수는 주어진 정규식 pattern을 문자열 dartResult에 적용하여, 모든 일치하는 부분을 리스트로 반환합니다.
  • 정규식에서 괄호 ( )로 감싼 부분은 각각 그룹으로 추출됩니다.

작동 예제

입력 문자열: "1S*2T*3S"

  1. 정규식 패턴이 문자열을 다음과 같이 매칭합니다:
    • (\d+): "1", "2", "3"
    • ([SDT]): "S", "T", "S"
    • ([*#]?): "*", "*", "" (옵션 없음)
  2. re.findall의 결과:
  3. python
    코드 복사
    matches = [('1', 'S', '*'), ('2', 'T', '*'), ('3', 'S', '')]

정규식 각 요소의 매칭

  • 첫 번째 점수 세트: 1S*
    • (\d+): "1"
    • ([SDT]): "S"
    • ([*#]?): "*"
  • 두 번째 점수 세트: 2T*
    • (\d+): "2"
    • ([SDT]): "T"
    • ([*#]?): "*"
  • 세 번째 점수 세트: 3S
    • (\d+): "3"
    • ([SDT]): "S"
    • ([*#]?): "" (옵션 없음)

결론

이 정규식과 re.findall을 사용하면 입력 문자열에서 점수, 보너스, 옵션을 깔끔하게 분리할 수 있습니다. 이를 통해 점수를 계산하거나 옵션 효과를 처리하는 로직을 간단하게 구현할 수 있습니다. 😊

https://school.programmers.co.kr/learn/courses/30/lessons/160586#

 

def solution(keymaps, targets):
    answer = []
    
    keyboard = {}
    for keymap in keymaps:
        for i, value in enumerate(keymap):
            if value not in keyboard:
                keyboard[value] = i + 1
            else:
                keyboard[value] = min(keyboard[value], i + 1)
    
    for target in targets:
        ans = 0
        for t in target:
            if t in keyboard: 
                ans += keyboard[t]
            else: 
                ans = -1 
        answer.append(ans)
    return answer

 

테스트 케이스 14번 부터 틀렸다.

여러 반례를 추가해 보다 아래 케이스에서 오류가 나는 것 발견

 

["ABCE"], ["ABDE"], [-1]

["BC"], ["AC", "BC"], [-1, 3] 

 

키보드에 없는 알파벳이 나오면 그대로 종료해야하는데 계속 값을 더해주고 있었다. 

 

아래는 수정 코드

def solution(keymaps, targets):
    answer = []
    
    keyboard = {}
    for keymap in keymaps:
        for i, value in enumerate(keymap):
            if value not in keyboard:
                keyboard[value] = i + 1
            else:
                keyboard[value] = min(keyboard[value], i + 1)
    
    for target in targets:
        ans = 0
        for t in target:
            if t in keyboard: 
                ans += keyboard[t]
            else: 
                ans = -1 
                break
        answer.append(ans)
    return answer

 

  1. 문자별 최소 키 입력 횟수 저장:
    • 먼저 keymap을 순회하면서 각 문자가 등장할 때 필요한 키 입력 횟수를 계산해 저장해.
    • 이 정보를 딕셔너리에 저장하면 매번 계산하지 않아도 되고 빠르게 조회할 수 있어.
  2. 여러 개의 keymap 처리:
    • 같은 문자가 여러 keymap에 존재할 경우, 가장 적은 입력 횟수를 저장해야 해.
    • 예를 들어, 'A'가 첫 번째 keymap에서 1번, 두 번째 keymap에서 3번에 등장한다면, 1을 저장해야 해.
  3. 문자열 작성 가능 여부 판단:
    • targets의 각 문자열에 대해, 해당 문자를 키보드에서 찾을 수 없으면 바로 -1을 결과로 저장해야 해.
    • 각 문자를 딕셔너리를 통해 빠르게 확인하고 키 입력 횟수를 누적 계산해.
  4. 최소 키 입력 횟수 계산:
    • targets의 각 문자열을 순회하며, 각 문자의 키 입력 횟수를 누적합으로 더해줘.
    • 한 문자열이라도 작성 불가능하면 바로 -1을 결과에 추가해.

단계별 정리

  1. 딕셔너리 char_min_presses = {}를 초기화.
  2. 각 keymap 문자열에 대해:
    • enumerate를 이용해 인덱스와 문자를 가져온다.
    • 딕셔너리에 해당 문자가 없다면 새로 추가하고, 있다면 최소값으로 업데이트한다.

https://school.programmers.co.kr/learn/courses/30/lessons/1845

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def solution(nums):
    answer = 0
    n = len(nums) // 2
    nums = len(set(nums))
    answer = min(nums, n)
    return answer

 

상황 정리

  • 연구실에 있는 폰켓몬의 종류를 나타내는 배열 nums가 주어집니다. 예) nums = [3, 1, 2, 3]
  • 총 N = len(nums)마리의 폰켓몬이 있습니다.
    • 여기서는 N = 4.
  • 홍 박사님이 준 조건: 최대 N/2마리를 가져갈 수 있습니다.
    • 여기서는 N/2 = 2.

목표

  • N/2마리의 폰켓몬을 선택할 때, 가능한 한 많은 종류를 선택합니다.

문제를 푸는 논리

1. 선택할 수 있는 최대 종류 수는 몇 마리인가?

  • 가져갈 수 있는 폰켓몬의 수는 항상 N/2입니다.
    • 즉, 종류가 아무리 많아도 최대 N/2 종류를 가져갈 수 있습니다.
  • 예) nums = [3, 1, 2, 3]일 때, 가져갈 수 있는 폰켓몬 수는 2마리입니다.

2. 폰켓몬 종류는 몇 종류인가?

  • 배열 nums에서 중복된 폰켓몬을 제거하면 고유한 종류의 수를 알 수 있습니다.
  • 예) nums = [3, 1, 2, 3] → 종류는 {3, 1, 2} → 3 종류입니다.

3. 선택할 수 있는 폰켓몬 종류의 최대값은 어떻게 결정될까?

  • 폰켓몬 종류 수와 가져갈 수 있는 수 중 작은 값을 선택하면 됩니다.
    • 왜냐하면:
      1. 폰켓몬 종류가 N/2 이상이면, N/2 종류만 선택 가능합니다.
      2. 폰켓몬 종류가 N/2보다 적으면, 모든 종류를 선택할 수 있습니다.
  • 예)
    • nums = [3, 1, 2, 3] (3종류, 가져갈 수 있는 수는 2): 최대 2종류 선택 가능.
    • nums = [3, 3, 3, 2, 2, 4] (3종류, 가져갈 수 있는 수는 3): 최대 3종류 선택 가능.

시간 복잡도 분석

  1. set(nums)로 중복 제거: O(N)
  2. len(nums) // 2 계산: O(1)
  3. 최종 계산과 반환: O(1)

전체 시간 복잡도: O(N)

이 접근법은 O(N^k)처럼 비효율적인 조합 계산을 피하기 때문에 큰 입력에서도 빠르게 작동합니다.

 

-----

처음에 조합 > set > 개수 카운팅을 하려고 했으나 시간 초과로 실패했다. 

가능한 모든 종류라는 조건에 사로잡혀 무조건적으로 조합만 생각했다. 

효율적으로 풀 수 있는 방법을 기억해놓자!

https://school.programmers.co.kr/learn/courses/30/lessons/147355

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def solution(t, p):
    answer = 0
    
    for i in range(len(t) - len(p) + 1):
        a = t[i:len(p)+i]
        
        if int(a) <= int(p):
            answer += 1
        i += 1 
        
    return answer

 

루프 범위: range(len(t) - len(p) + 1)

  • len(t): 문자열 t의 전체 길이.
  • len(p): 문자열 p의 길이 (부분 문자열의 길이).

루프의 목적은 문자열 t에서 길이가 len(p)인 부분 문자열을 모두 추출하는 것입니다.


왜 len(t) - len(p) + 1인가?

  • 부분 문자열을 추출하는 범위를 생각해봅시다.
  • 문자열 t의 인덱스 i에서 t[i:i+len(p)]로 길이가 len(p)인 부분 문자열을 추출합니다.
  • 예를 들어:
    • t = "3141592", p = "271"인 경우 p의 길이는 3입니다.
    • 첫 번째 부분 문자열은 t[0:3] = "314",
    • 두 번째 부분 문자열은 t[1:4] = "141",
    • 마지막 부분 문자열은 t[4:7] = "592".

인덱스 범위의 끝:

  • 부분 문자열의 마지막 인덱스는 i + len(p) - 1입니다.
  • 즉, i는 len(t) - len(p)까지만 반복해야 부분 문자열의 길이가 len(p)를 유지합니다.

+1을 추가하는 이유:

  • range는 끝값을 포함하지 않으므로, len(t) - len(p)까지 포함하려면 +1을 더해야 합니다.

 

https://school.programmers.co.kr/learn/courses/30/lessons/77484

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

def min_grade(nums, wins):
    tmp = len(list(set(wins) & set(nums)))
    if tmp < 2:
        return 6
    else:
        return (6 - tmp) + 1

def solution(lottos, win_nums):
    answer = []
    zero = lottos.count(0)
    max_g = (6 - (len(list(set(lottos) & set(win_nums))) + zero)) + 1
    min_g = min_grade(lottos, win_nums)

    if max_g == 7:
        max_g -= 1
        
    answer.append(max_g)
    answer.append(min_g)

    return answer

 

제한 시간 초과 20분

*반례 : [1, 2, 3, 4, 5, 6], [20, 9, 23, 12, 54, 60], [6, 6] 

0이 포함되어 있지 않은 로또 숫자가, 당첨 숫자와 다를 경우를 생각하지 못했다가 5분 남기고 생각났다 ㅠㅠ

하지만 해냈죠?

 

< 힌트 >

  • lottos와 win_nums의 교집합 크기를 계산.
  • zero의 개수를 더해 최고 순위 계산.
  • 교집합 크기만으로 최저 순위 계산.
  • grade_check를 호출하여 최고 순위와 최저 순위를 계산.

< 개선 >

주요 아이디어

  1. lottos에서 0의 개수와 win_nums와 겹치는 숫자의 개수를 바로 계산.
  2. 최고 순위는 (맞춘 숫자 + 0의 개수)로 계산.
  3. 최저 순위는 (맞춘 숫자)로 계산.
  4. 등수는 맞춘 개수에 따라 바로 매핑되도록 배열로 정의.

코드

def solution(lottos, win_nums):
    # 맞춘 개수와 0의 개수
    match_count = len(set(lottos) & set(win_nums))
    zero_count = lottos.count(0)

    # 등수 매핑 (0~6 맞춘 개수)
    rank = [6, 6, 5, 4, 3, 2, 1]

    # 최고 등수와 최저 등수 계산
    max_rank = rank[match_count + zero_count]
    min_rank = rank[match_count]

    return [max_rank, min_rank]

개선 포인트

  1. 등수 매핑 배열 사용:
    • 조건문 없이 배열을 사용하여 직관적으로 등수를 계산.
  2. 불필요한 변수 제거:
    • max_g나 min_g처럼 최대/최소를 비교하는 변수를 제거.
    • set 연산으로 교집합을 바로 계산해 간결화.
  3. 명확한 변수명:
    • match_count와 zero_count로 코드의 의도를 명확히 전달.

작동 과정

  1. set(lottos) & set(win_nums)로 일치하는 번호 개수를 빠르게 찾음.
  2. zero_count로 최고 가능한 맞춘 개수 계산.
  3. 미리 정의된 rank 배열에서 등수를 바로 가져옴.

+ Recent posts