본문 바로가기

알고리즘/프로그래머스

프로그래머스 - 숫자 야구(Python)

https://programmers.co.kr/learn/courses/30/lessons/42841

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

완전 탐색 문제입니다. 어려운 내용은 없지만 문제의 조건을 그냥 지나치기 쉽기 때문에 차근차근 읽어봐야 합니다.

 

solution 함수의 인자는 baseball이며 길이 1 이상 100 이하의 list 형태입니다.

baseball의 원소는 [세 자리의 수, 스트라이크 수, 볼의 수]이고요. 가능한 모든 숫자에서 세 자리 숫자의 스트라이크와 볼을 만족하는 숫자만을 살리거나, 만족하지 못하는 숫자들을 버리면 쉽게 풀 수 있습니다.

 

각자 서로 다른 1~9까지 3자리 임의의 숫자를 정한 뒤 서로에게 3자리의 숫자를 불러서 결과를 확인합니다. 그리고 그 결과를 토대로 상대가 정한 숫자를 예상한 뒤 맞힙니다.

 

각자 서로 다른 수여야 하고, 1~9까지의 범위를 가집니다. 세 자리의 숫자는 100부터 999까지가 가능하겠으나 조건이 있기에 시작은 123, 끝은 987이 되겠네요.

 

먼저 가능한 숫자들을 전부 골라보겠습니다.

def solution(baseball):
  answer = set(str(x) for x in range(100, 1000) if '0' not in str(x)
                   and str[x][0] != str[x][1] and str[x][1] != str[x][2] and str[x][0] != str[x][2])

조금 식이 길긴 하지만 100부터 999까지의 숫자 중 0을 포함하고 있지 않으며 3자리 숫자가 모두 다른 숫자만이 answer라는 집합의 원소가 됩니다.

 

 

    for base in baseball:
        num, strike, ball = base[0], base[1], base[2]
        notAnswer = set()
        for ans in list(answer):
            strikeBall = game(str(num), str(ans))
            if strikeBall[0] != strike or strikeBall[1] != ball:
                notAnswer.add(ans)
        answer = answer - notAnswer

baseball의 원소 하나하나씩 게임을 진행합니다.

answer는 set이기 때문에 반복문을 돌리기 위해 list형태로 변환시켰고, 반환되는 strikeBall의 첫 번째 원소 strike가 base가 가지고 있는 결과와 맞지 않거나 두 번째 원소 ball이 맞지 않는다면 notAnswer에 포함시킵니다.

한 게임이 끝났을 때 답이 될 수 없는 notAnswer의 원소들을 answer에서 전부 빼면서 answer를 갱신합니다.

 

 

def game(compare, ans):
    ball, strike = 0, 0
    numCountDict = {}
    for i in range(3):
        if ans[i] == compare[i]:
            strike += 1
        else:
            numCountDict[ans[i]] = numCountDict.get(ans[i], 0) + 1
            numCountDict[compare[i]] = numCountDict.get(compare[i], 0) + 1

    for key, value in numCountDict.items():
        if value > 1:
            ball += 1

    return [strike, ball]

게임을 진행하는 game 함수입니다. ans와 compare를 비교해 strike 개수와 ball 개수를 return 합니다.

먼저 ans[i]와 compare[i]가 같다면 strike이므로 strike에 1을 더해줍니다.

 

그렇지 않을 경우에는 numCountDict[해당 숫자]에 1을 더합니다. numCountDict의 value에는 ans와 compare에서 1~9까지의 숫자가 몇 번이나 나왔는지 기록됩니다.

 

게임이 끝난 후 numCountDict의 value값에서 2번 이상 나온 숫자가 있다면 ball에 1을 더해줍니다. 위치가 다른데 2번 이상 나왔다는 건 ball에 해당하니까요.

 

마지막으로 solution 함수에서 answer의 길이를 return 해주면 문제는 끝납니다.

 

전체 Python 코드

def game(compare, ans):
    ball, strike = 0, 0
    numCountDict = {}
    for i in range(3):
        if ans[i] == compare[i]:
            strike += 1
        else:
            numCountDict[ans[i]] = numCountDict.get(ans[i], 0) + 1
            numCountDict[compare[i]] = numCountDict.get(compare[i], 0) + 1

    for key, value in numCountDict.items():
        if value > 1:
            ball += 1

    return [strike, ball]

def solution(baseball):
    answer = set(str(x) for x in range(100, 1000) if '0' not in str(x)
                 and str[x][0] != str[x][1] and str[x][1] != str[x][2] and str[x][0] != str[x][2])
    for base in baseball:
        num, strike, ball = base[0], base[1], base[2]
        notAnswer = set()
        for ans in list(answer):
            strikeBall = game(str(num), str(ans))
            if strikeBall[0] != strike or strikeBall[1] != ball:
                notAnswer.add(ans)
        answer = answer - notAnswer
        
    return len(answer)

 

 

느낀 점

문제의 조건을 더욱 꼼꼼히 읽어야겠습니다. 사전에도 이야기했지만 문제의 조건을 제대로 읽지 않아 그리 어렵지 않은 문제임에도 애를 조금 먹었습니다. 처음에는 "서로 다른" 을 못 봐서 서로 다른 숫자들로만 구성했다가 또 답이 맞지 않아 조건을 다시 보니 1~9까지의 숫자더군요. 저는 0을 포함시키고 있었습니다.

 

실제 기업 코딩 테스트에서는 테스트 케이스를 그리 많이 주지 않습니다. 보통 3개 정도 주는 것 같고 그 이하로 주는 경우도 봤고요. 테스트 케이스가 없어 정답 확인이 어렵더라도 동작원리에서 누락되는 부분이 없도록 꼼꼼하게 코딩해야겠습니다.

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

프로그래머스 - 지형 이동  (0) 2020.05.21
프로그래머스 - 네트워크  (0) 2020.04.06
프로그래머스 - 베스트 앨범  (0) 2020.02.23