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

 

프로그래머스

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

programmers.co.kr

 

class Solution {
    fun solution(ingredient: IntArray): Int {
        var answer: Int = 0
        var stack = mutableListOf<Int>()
        
        for (i in ingredient) {
            stack.add(i)
            if ( stack.size >= 4 && stack.slice(stack.size-4 until stack.size) == listOf(1,2,3,1) ) {
                repeat(4) {stack.removeLast()}
                answer++
            }
        }
        return answer
    }
}

 

알고리즘 문제를 풀면서 처음으로 Stack 자료 구조를 사용해봤다.

 

코틀린에선 스택이 구현되어있지 않아서 사용하려면 자바에서 가져오거나 스스로 구현해야한다.

 

자바에서 가져올 땐 java.util.Stack으로 가져오면 된다.

그럼 var stack = Stack<Int>() 이런 형태로 사용가능하다.

 

위 문제는 배열의 숫자를 스택에 넣어서 스택 안에 1,2,3,1 형태로 완전한 버거형태가 들어오면 카운트해주고 스택에서 하나씩 제거해주면 해결된다.

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

 

프로그래머스

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

programmers.co.kr

 

 

class Solution {
    fun solution(s: String, skip: String, index: Int): String {
        var answer: String = ""
        val alphabet = mutableListOf('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z')
        alphabet.removeAll { skip.contains(it) }
        answer = s.map {
            alphabet[(alphabet.indexOf(it) + index) % alphabet.size]
        }.joinToString("")
        return answer
    }
}

 

나는 이런식으로 알파벳을 모두 리스트에 넣고 removeAll을 통해서 삭제시킨 다음 시작했다.

근데 더 깔끔한 방법이 있었다.

 

class Solution {
    fun solution(s: String, skip: String, index: Int): String {
        val alphabet = ('a'..'z').filter{ it !in skip }
        return s.map { alphabet[(alphabet.indexOf(it) + index) % alphabet.size] }.joinToString("")
    }
}

 

저런식으로도 되는구나...

 

맵 안에를 설명하자면 간단하다.

 

s 안을 순회하며 it이 알파벳의 몇번째 인덱스에 속하는지 값과 건너뛰어야 하는 index값을 더하고 알파벳 배열인덱스를 초과할 수 있기 때문에 배열 사이즈로 나머지 연산을 해준다. 이후 리턴 타입이 스트링이므로 joinToString()을 통해 타입을 맞춰준다.

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

 

프로그래머스

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

programmers.co.kr

 

class Solution {
    fun solution(keymap: Array<String>, targets: Array<String>): IntArray =
        targets.map { str ->
            str.map { c -> keymap.map { it.indexOf(c) + 1 }
                .filterNot { it < 1 }
                .let { list ->
                    if (list.isEmpty()) -1
                    else list.minOf { it }
                }
            }.let { if ( it.contains(-1)) -1 else it.sum() }
        }.toIntArray()
}

 

좋은 코드를 짜려면 좋은 코드를 많이 보라는 말에 따라 오늘부터 코틀린스러운 문제 해결법에 집중해서 다른 분의 코드를 분석하고 내 것으로 만드는 과정을 시작해보려 한다. 위 코드를 통해 코틀린의 각종 확장함수와 함수 표현식을 알 수 있다.

 

위의 코드는 프로그래머스 최용건님의 코드이다. 안에 내용을 안과 밖을 구분해서 분석해보자.

targets.map { str ->
str.map { c -> keymap.map { it.indexOf(c) + 1 }
                .filterNot { it < 1 }
                .let { list ->
                    if (list.isEmpty()) -1
                    else list.minOf { it }
                }

분석해보자면 .map으로 타겟 배열을 스트링 그리고 문자로 나눠서 그 문자가 keymap배열의 몇번째 인덱스에 있는지 확인한다.

 

이 후 .filterNot을 통해 인덱스가 1보다 작은 것들은 빼고 배열형태로 반환한다.

 

그리고 리스트안에 만약 문자가 없으면 -1 을 보내고 여러개 있는데 그 중 우리는 최솟값을 찾는 것이기에 minOf를 통해 작은 값을 보낸다.

targets.map { str ->

				...
                
}.let{ if(it.contains(-1)) -1 else it.sum() }

 

나온 리스트안에 -1이 포함되어있으면 -1을 내보내고 아니라면 리스트안에 원소들을 모두 .sum()을 통해 합한다.

 

내가 생각하기엔 코틀린스럽다는 말이 이제 조금 와닿고 있다. 아무래도 이 문제에 최적화된 코드는 아니지만 문제가 좀 더 꼬여있을 때를 생각하면 좀 더 확장성 있게 잘 짠 코드인 것 같다.

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

 

프로그래머스

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

programmers.co.kr

 

 

class Solution {
    fun solution(s: String): Int {
        var answer: Int = 0
        var string = s.toList()
        var countA = 0
        var countB = 0
        for (i in 0 until string.size) {
            val firstChar = string[0]
            if (string[i] == firstChar) {
                countA++
                if (countA == countB) {
                    break
                }
            } else if(string[i] != firstChar){
                if (countA == countB) {
                    break
                }
                countB++
            } else {
                answer = 1
                return answer
            }
        }
        if(countA == 1) {
            answer = (s.length/2) + (s.length%2)
        } else {
            val lastS = s.length - countA - countB
            answer = (lastS / countA) + 2
        }
        return answer
    }
}

 

코드를 분석해보면 알겠지만 이건 문제 파악을 잘못해서 생긴 코드다. 첫글자만 가지고 전체 문자열을 판단하는 것으로 생각하고 코드를 짜서 봤더니 테케는 통과했지만 제출케이스에선 주르륵 빨간불이 들어왔다. 

 

도무지 이해가 안되서 내용을 다시 천천히 살펴본 결과,, 문자열 분리 후 다시 첫 글자를 가져와서 판단하는 것이었다.

문제를 똑바로 읽자... 제발... 1시간 넘게 내다 버렸다...

class Solution {
    fun solution(s: String): Int {
        var firstChar = s[0]
        var countA = 0
        var countB = 0
        var result = 0
        var reset = false

        s.forEachIndexed { index, char ->
            if (reset) {
                firstChar = char
                reset = false
            }

            if (char == firstChar) countA ++
            if (char != firstChar) countB ++

            if (countA == countB || index == s.lastIndex) {
                result += 1
                reset = true
            }
        }
        return result
    }
}

 

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

 

프로그래머스

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

programmers.co.kr

 

 

class Solution {
    fun solution(n: Int, lost: IntArray, reserve: IntArray): Int {
        var answer = 0
        var rent = reserve.toMutableList()
        var need = lost.toMutableList()
        var count = 0
        lost.map {
            if (reserve.contains(it)) {
                rent.remove(it)
                need.remove(it)
            }
        }
        need.sort()
        for (i in need) {
            if (rent.contains(i - 1)) {
                count++
                rent.remove(i - 1)
                continue
            }
            else if (rent.contains(i + 1)) {
                count++
                rent.remove(i + 1)
                continue
            }
            else {
                continue
            }
        }
        answer = n - need.size + count 
        return answer
    }
}

 

문제는 그리 어려운 문제가 아니었으나,, 반례 때문에 좀 헤맸다.. 내 코드에서는 테스트 13, 14번의 케이스가 lost 배열을 정렬해주어야 풀린다.  

 

테스트 13, 14 의 반례는 4 [4,2] [3,5] 이다.

 

우선 체육복 여분을 가져온 학생이 도난을 당한 경우를 제하고 도난 당한 학생 배열을 정렬해준 뒤 도난 당한 학생의 값보다 작은 여분 가져온 학생 배열을 우선적으로 찾아주는 방식으로 했다. 시간은 20ms 안쪽으로 나왔다.

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

 

프로그래머스

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

programmers.co.kr

 

 

이번 문제는 풀지 못했다... 가면 갈수록 어려워지고 있다. 다른 분의 코드를 보면서도 이해가 가지 않아 분석을 하면서 공부를하고 실력을 키우는 방향으로 잡았다.

 

import kotlin.math.min

class Solution {
    fun solution(X: String, Y: String): String {
        var answer: String = ""

        for (ch in (9 downTo 0).toList().map { it.toString() }) {
            answer += ch.repeat(
            min(
            	X.count { it.toString() == ch },
            	Y.count { it.toString() == ch }
               	)
           )
        }
        if (answer.isEmpty()) answer = "-1"
        if (answer.toList().distinct() == listOf('0')) answer = "0"

        return answer
    }
}

 

우선 이 코드의 방식은 9 부터 0 까지의 숫자로 X와 Y에서 등장하는 횟수를 구한 뒤 둘의 최솟값으로 반환하여 answer에 넣어주는 방식이다.

 

이제 한줄 한줄 분석해보자.

 

더보기

for (ch in (9 downTo 0).toList().map { it.toString() })

 

여기서는 9 downTo 0 을 통해 9 8 7 6 5 4 3 2 1의 범위를 가져오고

 

이 숫자들을 toList()를 통해 리스트 형태인 [ 9, 8, 7, 6, 5, 4, 3, 2, 1 ] 이 형식으로 만들어준다.

 

이후 X와 Y의 문자열 형태와 비교시켜주기 위해 .map{it.toString()} 각각의 숫자를 문자열형태로 변환해준다.

-> ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0']

 

그리고 각 문자열 '9', '8', '7', '6', '5', '4', '3', '2', '1', '0' 을 차례대로 ch에 담아 준다.

더보기

answer += ch.repeat(코드내용)

여기가 처음 의아했던 부분이었다.

ch.repeat(n)은 앞에 변수인 ch를 n번 반복시켜주는 함수다.

이를 통해 repeat안에는 Int값이 들어올 것임을 알 수 있다.

아무튼 9부터 시작하여 0의 문자를 들어오는 정수 값에 따라 여러번 Answer에 넣어줄 수 있게 만들어주고 있다.

나머지는 뒤에 코드를 같이 보며 설명하겠다.

더보기

 

ch.repeat(min(X.count { it.toString() == ch }, Y.count { it.toString() == ch }))

 min() 함수는 ()안에 두 값을 비교해 최소값을 반환해주는 함수이다.

 중요한 건 그 안에 내용이다.

X.count{it.toString() == ch} Y.count{it.toString() == ch}

실제 함수가 돌아가는 예를 보자, X 가 "5525" Y가 "1255" 

처음 ch인 '9'가 들어온다. 그리고 X와 Y의 문자열을 순회하며 '9'와 같은 값이라면 카운트를 해주는 것이다.

X와 Y모두 '9', '8', '7', '6' 을 가지고 있지 않으므로 위의 ch.repeat에서 '9', '8', '7', '6' 은 들어가지 않는다.

하지만 '5' 차례일 때 부터는 다르다. X에서는 '5'가 3번이나 들어가고 Y는 2번 들어간다. 우리는 짝꿍일 경우에만 넣어주기에 2개만 넣어주어야 한다. 그걸 위해 min 함수를 통해 둘의 최소값인 Ycount=2를 반환해주는 것이다.

여기서 it.toString은 ch와 비교연산을 위해 타입을 맞춰주는 것이다. (원래 it 은char타입임)

더보기

        if (answer.isEmpty()) answer = "-1"
        if (answer.toList().distinct() == listOf('0')) answer = "0"

이 후 맞는 숫자가 없을 경우에는 answer에 어떤 ch도 들어가지 않기에 비어있으므로 -1 을 반환

그리고 X 가 "100" Y가 "10025"  와 같이 answer에 '0' 값이 두번 이상 있는 경우에 대비해 .distinct()로 중복을 제거해준 후 '0'만 있다면 answer에 '0'을 반환해주는 처리를 해준다. 

 

 

 

+ Recent posts