본문 바로가기
♞ | 공부일지

[Android, 내일배움캠프] 공부일지(2024-05-30)

by immgga 2024. 5. 30.
오늘 공부한 내용 정리(2024년 5월 30일)

 

 

1. 코드카타 문제풀이

A. 둘만의 암호

문제 내용

 

문제 풀이 방법

  • s의 각 문자들을 index만큼 뒤로 옮기기(a -> b -> c...)
  • 옮기는 문자들 중에 skip에 문자가 포함되어 있으면 그 문자를 제외(건너뜀)
  • 문자가 z를 넘어가면 a로 다시 돌아가서 옮기기 시작.

 

해결 코드(스포 주의)

더보기
// s의 각 문자를 index만큼 뒤로 옮기기
// 옮기는 문자가 skip에 포함되어 있는 경우, 제외하고 넘어가기
// 문자가 z를 넘어가면 a로 돌아가서 다시 시작.
fun solution(s: String, skip: String, index: Int): String {
    var answer: String = ""
    var move = 0
    val rollbackCode = 97

    s.forEach { char ->
        var charCode = char.code
        while (move < index) {
            charCode++
            if (charCode > 122) charCode = rollbackCode
            if (skip.contains(charCode.toChar())) continue
            move++
        }
        move = 0

        answer += charCode.toChar()
    }

    return answer
}

 

풀이 과정

  • s의 각 문자들을 code 화해서 charCode에 저장(a = 97, z = 122)
  • charCode를 1씩 더하고, charCode가 122를 넘어가면(z를 넘김) a로 롤백하고, skip에 charCode.toChar()가 포함되어 있으면 건너뛰기.
    두 조건을 모두 수행하고 나면 얼마나 글자를 이동시켰는지에 대한 변수인 move를 +1. 이를 move가 index와 같아질 때까지 반복.
  • while을 빠져나온 후, move를 0으로 초기화하고 answer에 charCode를 Char로 바꿔서 넣어주기.

 

문제 해결 과정

  • z를 넘어갔을 때의 조건과, skip이 charCode를 Char로 바꿨을 때 포함되는지 확인하는 조건의 순서를 올바르게 하지 않았었다.
    만약 문자가 z이고 skip이 za 일 때 포함 조건을 z를 넘어갔을 때 조건보다 먼저 쓰게 되면 z -> { 가 되는데 포함 조건을 넘어가게 돼서 z -> {(a)가 된다 정상적인 동작은 z -> a -> b가 되어야 한다.
    z를 넘어갔을 때의 조건을 먼저 사용하면 z(122)를 넘어가면 바로 a로 바꾸고 그럼 정상적으로 skip에 문자가 포함되어 있는지 체크할 수 있다.

 

2. 개인 공부

A. 괄호(백준, 9012번)

문제 내용

 

문제 풀이 방법

  • 괄호 문자열이 주어졌을 때, 괄호 문자열이 올바른 괄호 문자열인지 확인해 return.
    "()"는 올바른 괄호 문자열이고, ")("는 올바르지 않은 괄호 문자열이다.

 

해결 코드(스포 주의)

더보기
import java.util.Scanner

fun main() = with(Scanner(System.`in`)) {
    val case = nextInt()

    repeat(case) {
        val parenthesis = next()
        val parenthesisList = parenthesis.toMutableList()
        var result = "YES"

        while (parenthesisList.contains('(')) {
            if (parenthesisList[0] == '(') {
                parenthesisList.removeAt(0)
                val matchIndex = parenthesisList.indexOf(')')

                if (matchIndex != -1) {
                    parenthesisList.removeAt(matchIndex)
                } else {
                    result = "NO"
                    break
                }
            } else {
                result = "NO"
                break
            }
        }

        if (parenthesisList.isNotEmpty()) result = "NO"

        println(result)
    }
}

 

풀이 과정

  • 괄호 문자열을 받는 parentgesis를 생성.
    parentgesis를 list로 변환한 parentgesisList도 생성.
  • parentgesisList에서 괄호의 시작인 '(' 데이터가 없을 때까지 반복한다.
  • parentgesisList의 첫 번째 데이터가 시작 괄호가 아니면 올바른 괄호 문자열이 될 수 없다.
  • 첫 번째 데이터가 시작 괄호면 끝 괄호 ')' 데이터가 어떤 index에 있는지 확인하고 parentgesisList의 데이터를 괄호에 맞게 지워 나간다.
  • 위 과정을 반복하면 while문을 빠져나오게 되는데, 이때 parentgesisList에 데이터가 남아 있으면 올바르지 않은 괄호 문자열이다.

 

B. 2 x n 타일링(백준, 11726번)

문제 내용

 

문제 풀이 방법

  • 세로가 2로 고정되어 있고 가로가 n인 직사각형을 1x2, 2x1 타일로 채우는 방법의 경우의 수를 구하기.
  • 경우의 수를 구하는 것이 아닌 경우의 수에 10007을 나눈 값을 출력해야 한다.

 

해결 코드(스포 주의)

더보기
import java.util.Scanner

fun main() = with(Scanner(System.`in`)) {
    val width = nextInt()
    var prev: Long = 0
    var curr: Long = 1

    // 0 1 2 3 5 8 13 21 34 55
    for (i in 1..width) {
        val temp = curr
        curr = (curr + prev) % 10007
        prev = temp
    }

    println(curr)
}

 

풀이 과정

  • 피보나치 수열 알고리즘을 이용한 풀이.
  • 재귀를 사용하면 숫자가 커질수록 재귀가 깊어져 시간 초과가 발생하기 때문에 반복문을 사용.
  • 피보나치 수열 결과 변수인 curr에 값을 넣고 나서 10007로 나눈 나머지를 넣어준다.

 

문제 해결 과정

  • 피보나치 수열에 대해 알지 못하면 풀기가 어려워 보이는 문제였다.
    문제를 피보나치 수열로 풀어야 한다는 것을 깨닫게 되면 공짜 문제가 된다.
  • n을 1부터 9까지 모든 경우의 수를 일일이 적어놓고 보니 피보나치 수열의 규칙과 흡사해서 피보나치 수열 알고리즘을 공부하고 문제를 해결하였다.

 

C. AC(백준, 5430번)

문제 내용

 

문제 풀이 방법

  • 테스트 케이스의 개수(T)와 수행할 함수가 주어진다. 함수는 R, D가 있다.
    배열의 길이와 배열 형태("[1,2,3....]")의 데이터를 string으로 입력받는다.
  • 입력받은 배열에서 함수에 맞는 기능을 수행한(R: 배열 뒤집기, D: 버리기) 결과를 출력.
    D를 수행할 때, 버릴 데이터가 없으면 error를 출력.

 

해결 코드(스포 주의)

더보기
import java.io.BufferedReader
import java.io.InputStreamReader

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val case = readLine().toInt()

    repeat(case) {
        val command = readLine()
        val arrLength = readLine().toInt()
        val inputArr = readLine()
        val arrStr = StringBuilder(inputArr.substring(1, inputArr.length-1))
        // string이 뒤집히는지 확인하기 위함.
        var isReserved = false
        var error = ""

        for (cmd in command) {
            when (cmd) {
                // true 배열이 뒤집힘.
                // false 배열이 원래 방향으로 돌아옴.
                'R' -> isReserved = !isReserved
                'D' -> {
                    if (arrStr.isEmpty()) {
                        error = "error"
                        break
                    } else {
                        if (isReserved) {
                            val comma = arrStr.lastIndexOf(",")

                            if (comma != -1) arrStr.delete(comma, arrStr.length)
                            else arrStr.deleteRange(0, arrStr.length)
                        } else {
                            val comma = arrStr.indexOf(",")

                            if (comma != -1) arrStr.delete(0, comma + 1)
                            else arrStr.deleteRange(0, arrStr.length)
                        }
                    }
                }
            }
        }

        val resultList = arrStr.split(",")
        if (error.isEmpty()) {
            if (isReserved) println("[${resultList.reversed().joinToString(",")}]")
            else println("[${resultList.joinToString(",")}]")
        }
        else println(error)
    }
}

 

풀이 과정

  • command(함수 명령어), arrLength(입력받을 배열의 길이), inputArr(배열 형태의 문자열)을 입력받는다.
    inputArr를 String형태로 변환한(대괄호 제거) arrStr 데이터 생성.
    command에서 R 함수의 기능을 수행했는지를 판단할 isReserved 생성.
    에러가 발생했을 때, "error" 문자열을 넣을 error 변수를 생성.
  • command의 문자를 반복문으로 돌려서 어떤 문자인지 확인한다.
    R인 경우는 isReserved를 반대로 저장한다. string에 reserved()를 안 쓴 이유는 문제 해결에 언급함.
  • D인 경우는 isReserved 상태에 따라 로직이 다르다.
    arrStr이 비어 있는 경우에 데이터가 없다는 뜻이 되기 때문에 "error"를 대입한다.
    arrStr 데이터는 1,2,3,4... 와 같은 콤마(,)가 포함된 문자열 데이터 형식이다.
    지워야 할 데이터의 콤마 위치를 알기 위해 comma 변수를 생성한다. isReserved가 true일 때는 뒤집기가 된 상태이므로 마지막 콤마 index를 불러오고, false일 때는 첫 번째의 index를 불러온다.
    comma 데이터가 존재할 경우(-1이 아님), arrStr에서 해당되는 숫자와 콤마를 같이 제거해 준다. -1인 경우는 숫자 데이터가 1개밖에 없거나 없는 경우이므로 완전 삭제를 해준다(deleteRange).
  • 함수 적용을 끝낸 arrStr에 split을 해서 list형태로 바꿔준다(resultList).
  • error가 비어 있으면(error가 없음) isReserved 상태에 따라 뒤집힌 상태(true)이면 이때 list에 reserved()를 해주고 string으로 전환해 준다(joinToString). 뒤집히지 않은 상태이면 reserved를 하지 않고 출력한다.

 

문제 해결 과정

  • 시간 초과와의 싸움.
    시간 초과를 해결하기 위해 처음에 arrStr을 입력받고 나서 list로 command의 로직을 수행하려 했지만 StringBuilder로 수행함.
    when 문에서 R일 때 무지성으로 reserved()를 하면 시간 초과가 나기 때문에 isReserved를 만들어 isReserved 상태에 따른 로직을 추가해 주었음.
  • command가 R이고, arrStr이 [12]일 경우 마지막 과정에 string을 list로 변환했다가 다시 string으로 변환하는 코드를 넣어주지 않으면 R이 수행되고 isReserved가 true이기 때문에 string에 reserved() 함수가 적용되는데, 출력이 다음과 같이 되게 된다. [21] 숫자 데이터는 1개밖에 없는데 위와 같은 오류가 나는 경우는 배열 데이터가 10 이상인 경우 reserved() 했을 때도 데이터가 정상적으로 들어가는지 확인하자.


D. 수 정렬하기(백준, 2751번)

문제 내용

 

문제 풀이 방법

  • 간단한 오름차순 정렬 문제. 하지만 숫자의 개수가 백만개인.
  • 입력이 백만 개까지 들어올 수 있어서 시간 초과의 우려가 큰 문제.

 

해결 코드(스포 주의)

더보기
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val case = readLine().toInt()
    val inputStr = StringBuilder()

    repeat(case) {
        val num = readLine()
        if (case-1 == it) inputStr.append(num)
        else inputStr.append("$num,")
    }

    val list = inputStr.split(",").map { it.toInt() }
    val bw = BufferedWriter(OutputStreamWriter(System.out))
    list.sorted().forEach {
        bw.write("$it\n")
    }
    bw.flush()
    bw.close()
}

 

풀이 과정

  • 숫자를 배열이 아닌 문자열로 입력받을 수 있도록 StringBuilder를 사용함.
  • case의 개수만큼 반복하면서 inputStr에 데이터를 추가한다. 마지막 반복인 경우 같이 추가했던 콤마(,)를 추가하지 않는다.
  • 바로 inputStr를 List<Int>타입으로 바꿔주고, bufferedWriter()를 이용해 println() 보다 더 빠른 출력을 할 수 있도록 했다.

 

문제 해결 과정

  • 시간 초과를 해결하기 위해
    배열로 입력 데이터를 저장 -> StringBuilder로 저장.
    println()으로 출력 -> bw.write()로 출력으로 변경했다.
  • 다른 사람들 풀이를 보고 정렬 알고리즘을 사용한 코드가 많았는데 안 써도 돼서 다행이었다.

 


 

오늘 공부 내용 정리 및 회고

  • 좀 티어가 높은 문제들을 풀어보았음.
  • 티어가 오를수록 프로그래밍 실력 말고도 문제를 보고 특정한 규칙을 찾아 해결하는 문제가 많이 나오는 듯하다.
  • 백준 silver 4 이상 문제부터 수학적 사고도 많이 필요할 것 같다. 수학 공식을 몰라서 검색을 해보는 경우가 많다.
728x90

댓글