[Programmers/Lv. 0] 코딩테스트 입문 Day 21 - 문자열, 사칙연산, 시뮬레이션, 2차원배열, 수학, 배열 (Python)

2023. 6. 4. 09:51Algorithm

1. 숨어있는 숫자의 덧셈 (2)


소문자, 대문자, 자연수로만 구성되어 있는 문자열 my_string이 주어질 때, my_string 안에 있는 자연수들의 합을 구하라는 문제이다.

단, 연속된 수는 하나의 숫자로 간주한다.
my_string 문자열에 자연수가 없는 경우에는 0을 반환한다.

 

(내가 생각한 풀이 과정)

어려운 코드는 아니지만, 설명이 조금 어려워 사용한 코드도 함께 설명을 하겠다.

우선, 문자열(my_string)을 하나씩 반복하면서 요소(i)가 숫자인지 아닌지를 판별(isdigit() 함수 사용)해준다.
이때, 가장 고려해야 할 점이 "연속된 수는 하나의 숫자로 간주한다"는 점이었다.
즉, 단순히 숫자라고 더하기를 더해주면 안 되고, 이후의 수, 그 이후의 수까지 고려를 해서 함께 합을 해야 한다는 점 때문에
별도의 문자열(num_string)로 분리했다가 정수가 아닐 경우(else문으로 빠질 경우)에 함께 추가(num_list.append(num_string))해줬다.

마지막에는 정수값에만 한해서(isdigit() 함수) 합해주면 문제를 해결할 수 있다.
-> 위의 코드를 돌리면 num_list에는 ["","","1","2","","3","4","","",""] 형태로 들어있을 거기 때문!

 

(코드)

더보기

💡 내 코드

def solution(my_string):
    num_list = []
    num_string = ""
    answer = 0
    
    for i in my_string:
        if i.isdigit():
            num_string += i
        else:
            num_list.append(num_string)
            num_string = ""
    num_list.append(num_string)  # my_string의 마지막 값이 정수로 끝내는 경우가 있을 수 있음!
    
    for num in num_list:
        if num.isdigit(): answer += int(num)
        
    return answer

 

💡 다른 사람의 풀이 코드

다른 사람의 풀이 코드를 보니까 내가 너무 어렵게 생각해서 풀은건가 싶었다.

둘째 줄 코드는 문자열을 하나씩 반복하면서, 해당 문자가 정수인지 아닌지를 판별하고, 정수일 경우에만 하나의 문자열로 join, 정수가 아닐 경우에는 ' ' 공란을 만드는 코드이다.
문자 하나씩 판별한다는 점에서는 나랑 아이디어는 같았지만, 정수가 아닌 경우에 띄어쓰기를 만들어주는 것이 달랐다.

이후 split()을 기준으로 합을 해주면 같은 답을 구할 수 있었다. 

def solution(my_string):
    s = ''.join(i if i.isdigit() else ' ' for i in my_string)
    return sum(int(i) for i in s.split())

 


2. 안전지대


아래 그림과 같이 지뢰가 있는 지역과 지뢰에 인접한 칸을 모두 위험지역으로 간주할 때, 
지뢰가 매설된 지역이 나타나있는 board 배열이 주어진다. 이때, 안전한 지역(=위험지역이 아닌 곳)의 칸 수를 구하라는 문제이다.

지뢰가 매설된 지역은 2차원 배열 안에 1로 표시되어 있고, 지뢰가 없는 지역은 0으로 표시한다.

board 배열은 가로와 세로의 길이가 같은 n * n 배열이다.

 

(내가 생각한 풀이 과정)

사전세팅이 먼저 필요한 문제이다.
첫번째로, 원래 배열과 변화된 배열을 계속해서 비교해야 된다고 생각했기에 배열을 하나 새로 복사했다. (new_board 배열)
두 번째로는 배열의 양 끝 부분을 파악하기 위해 upper(위), lower(밑), left(왼), right(오) 변수를 만들어줬다.

이제 배열을 하나하나 비교해 보면서, 지뢰가 있는지를 확인하며 위험 지역을 판별해 보겠다.
이때, 배열을 비교할 때는 새로 변화된 곳을 제외하고, 기존 배열만을 기준으로 비교해야 하기 때문에 사전세팅 1번으로 복사해 준 새로운 배열을 이용해 준다.
(이게 무슨 말인고 하면, 배열을 하나씩 비교하면서 위험지역도 1로 표시를 해줄 것이기 때문에, 나중에는 결국 기존 지뢰가 있던 지역과 구분이 되지 않을 것이기 때문이다.)

왼쪽 위부터 순서대로 1, 2... 8번 구역까지 있다고 가정할 때, 파악한 배열의 양 끝 부분이 아닌지를 판별하며 위험지역을 판단한다.
1번 구역은 위와 왼쪽, 2번 구역은 위, 3번 구역은 위와 오른쪽,
4번 구역은 왼쪽, 5번 구역은 오른쪽,
6번 구역은 아래와 왼쪽, 7번 구역은 아래, 8번 구역은 아래와 오른쪽이 경계선이 아닌지 파악하면서 해당되는 구역을 1로 바꿔주면 된다.

마지막으로, 전체 board 배열을 반복하면서 안전 구역(0)을 카운트해 준다.

 

(코드)

더보기

💡 내 코드

def solution(board):
    answer = 0
    
    new_board = []
    for b in board:
        new_board.append(b.copy())
    
    upper, lower, left, right = 0, len(board)-1, 0, len(board[0])-1
    
    for out_ar in range(len(board)):
        for in_ar in range(len(board[0])):
            if new_board[out_ar][in_ar] == 1:
                if out_ar != upper and in_ar != left:
                    board[out_ar-1][in_ar-1] = 1
                if out_ar != upper:   
                    board[out_ar-1][in_ar] = 1
                if out_ar != upper and in_ar != right:
                    board[out_ar-1][in_ar+1] = 1
                if in_ar != left:
                    board[out_ar][in_ar-1] = 1
                if in_ar != right:
                    board[out_ar][in_ar+1] = 1
                if out_ar != lower and in_ar != left:
                    board[out_ar+1][in_ar-1] = 1
                if out_ar != lower:
                    board[out_ar+1][in_ar] = 1
                if out_ar != lower and in_ar != right:
                    board[out_ar+1][in_ar+1] = 1
            
    for lst in board:
        answer += lst.count(0) 
    return answer

 


3. 삼각형의 완성조건 (2)


선분 세 개로 삼각형을 만들기 위해서 만족해야 하는 조건이 있다.
"가장 긴 변의 길이는 다른 두 변의 길이의 합보다 작아야 한다."

삼각형의 두 변의 길이가 담긴 배열이 주어질 때, 나머지 한 변이 될 수 있는 정수의 개수를 구하라는 문제이다.

 

(내가 생각한 풀이 과정)

정수의 개수를 구하기 위한 answer 변수를 한 개 만들고,
두 선분의 길이만 있는 sides 배열에 0 요소를 추가해 3개의 선분을 배열 안에 모두 포함시키자.

이제, 추가한 0 요소를 1부터 기존 두 선분의 합까지의 범위를 반복을 하면서 삼각형을 만들 수 있는지 확인해 줄 거다.

조건은, "가장 긴 변의 길이는 다른 두 변의 길이의 합보다 작아야 한다."
즉, 가장 긴 변의 길이(max(sides))가 다른 두 변의 길이의 합(sum(sides)-max(sides))보다 작은지(<)를 확인하면 되는 것이다.

기존 두 선분의 합까지로 범위를 설정한 이유는
새로 추가한 선분이 가장 긴 변의 길이가 되더라도 다른 두 변의 길이의 합보다 작은 경우에만 삼각형이 될 수 있기 때문에, 이 이후의 범위는 보지 않더라도 삼각형을 만들 수 없는 것이 뻔하기 때문이다.

 

(코드)

더보기

💡 내 코드

def solution(sides):
    answer = 0
    sides.append(0)
    
    for i in range(1, sides[0]+sides[1]):
        sides[2] = i
        if max(sides) < (sum(sides)-max(sides)): answer += 1
    
    return answer

 

💡 다른 사람의 풀이 코드

아래 코드는 수학적으로 문제를 해석해서 푼 방법이다.

기존 알고 있던 변이 a, b라고 가정할 때 (a > b), 새로 입력받는 c는 a보다 클 수도, 작을 수도, 같을 수도 있다.
위 세 가지의 경우를 기준으로 식을 나눠본 것이다.

1) c가 a보다 큰 경우 (c > a > b)
a + b > c 이므로, a + b > c > a가 된다. 그러므로, 가능한 c의 개수는 b - 1개가 된다.

2) c가 a보다 작은 경우 (a > b, a > c)
b + c > a 이므로, b + c > a, c가 된다. 그러므로, 가능한 c의 개수는 b - 1개가 된다.

3) c와 a가 같은 경우 (c=a 한 가지 경우만 존재)

그러므로, 모든 경우의 수를 합하면 (b - 1) + (b - 1) + 1 = 2 * b - 1개이다.
이 문제 풀이 초기에 a > b라고 가정했기 때문에, b 선분은 배열에서 최소 값이라고 볼 수 있으므로, min(sides)로 치환 가능하다. 

def solution(sides):
    return 2 * min(sides) - 1

 


4. 외계어 사전


알파벳이 담겨있는 배열 spell과 외계어 사전 배열 dic이 주어진다.
spell에 담긴 알파벳을 한 번씩만 모두 사용한 단어가 dic에 존재하면 1을, 존재하지 않는다면 2를 반환하라는 문제이다.

모든 원소는 알파벳 소문자로만 이루어져 있다.
spell의 원소의 모두 사용해 만들 수 있는 단어는 dic에 두 개 이상 존재하지 않으며, 각 배열 모두 중복된 원소를 갖지 않는다.

 

(내가 생각한 풀이 과정)

dic에 들어있는 문자열 단어들을 반복하면서, 알파벳이 담겨 있는 배열 spell의 단어들과 비교를 해줄 거다.
순서대로 알파벳이 모두 있는지 확인하고, 마지막에 길이도 같은지 비교해 주면 된다.

 

(코드)

더보기

💡 내 코드

def solution(spell, dic):    
    for i in range(len(dic)):
        r = 0
        for j in spell:
            if j in dic[i]: r += 1 
        if r == len(spell): return 1
    return 2

 

💡 다른 사람의 풀이 코드

중복값이 들어있지 않다는 배열의 특성을 잘 이용한 코드이다.

def solution(spell, dic):
    spell = set(spell)
    for s in dic:
        if not spell-set(s):
            return 1
    return 2