LeetCode발행일 2025. 1. 15.원본 https://blog.naver.com/jword_/223726501399 ↗

Kotlin/Python String, Char, Int변환 연습 (프로그래머스 - 동영상 재생기)

Kotlin/Python String, Char, Int변환 연습 (프로그래머스 - 동영상 재생기) — #kotlinString #kotlinInt #kotlinchar #pythonstring #pythonChar #pythonInt #프로그래머스 #동영상...

#LeetCode#Naver Blog

#kotlinString #kotlinInt #kotlinchar #pythonstring #pythonChar #pythonInt #프로그래머스 #동영상재생기

​

​

  • 사용된 언어: 코틀린, 혹은 파이썬
  • 순서: 로직, 코드 구현, 코드 분석

문자열 조작하기

프로그래머스 PCCCP 기출문제 1번 - 동영상 재생기

코테 문제를 풀다보면 문자열을 조작하거나, 특정 패턴을 찾는 문제가 많이 나온다. 아무래도 가장 많이 사용되는 부분이라 그런 것 같다. 이번 문제를 보자마자, 이건 문자열을 마음대로 잘 요리할 수 있는지에 대해 묻는 문제라는 것을 알았다.

​

문제의 풀이는 되게 쉬워보였는데, 막상 코드를 작성하려고 하니 내 맘대로 조작이 쉽지가 않았다. \\

​

1차 시도

프로그래머스 PCCCP 기출문제 1번 - 동영상 재생기

javascript 코드 예제
                                    💡 문제 풀이 방법
1. "59:59"형태를 5959로 만들기
- 모든 입력은 String으로 들어오는데, :를 제거해야 정수로 변환할 수 있다.
- 정수로 변환하는 이유는 command를 더 쉽게 처리할 수 있기 때문이다.

2. 정수 연산
- 모든 연산은 정수 연산으로 진행한다.
- 경계갑 처리를 해준다 (현재 위치 <= 0 = 0, 현재 위치 > 1

3. 오프닝 체크
- 커맨드 진행동안 모든 연산에 대해 오프닝을 체크한다.(가장 우선적으로)
- start <= now <= end라면 end로 이동후 다음 커맨드를 진행
javascript 코드 예제
                                    class Solution {

    fun seperateTime(time: String): Int {
        val intBuilder: StringBuilder = StringBuilder()
        intBuilder.append(time[0])
        intBuilder.append(time[1])
        intBuilder.append(time[3])
        intBuilder.append(time[4])

        return intBuilder.toString().toInt()
    }

    fun solution(video_len: String, pos: String, op_start: String, op_end: String, commands: Array<String>): String {
        val totalLen = seperateTime(video_len) // Pair<Int, Int>
        var position = seperateTime(pos)
        val opStart = seperateTime(op_start)
        val opEnd = seperateTime(op_end)

        for (command in commands) {
            // chekc op
            if (opStart <= position && opEnd <= position) {
                position = opEnd
            }

            // do command
            if (command == "next") {
                position += 10
                if (position >= totalLen) {
                    position = totalLen
                }
            } else {
                position -= 10
                if (position <= 0) {
                    position = 0
                }
            }
            // adjust carry -> use new String Builder
            var adjustList: MutableList<Char> = mutableListOf()
            for (t in position.toString()) {
                adjustList.add(t)
            }

            for (t in adjustList) {
                if (t == '6') {
                    adjustList[1] = (adjustList[1].digitToInt() + 1).toString()[0]
                    adjustList[2] = (adjustList[2].digitToInt() - 6).toString()[0]
                }
            }
            println("adjustList: ${adjustList}")
            position = adjustList.toString().toInt()
        }
        var resultBuilder = StringBuilder()
        for ((i, t) in position.toString().withIndex()) {
            resultBuilder.append(t)
            if (i == 1) {
                resultBuilder.append(':')
            }
        }
        return ""
    }
}
javascript 코드 예제
                                    💡 발견한 여러 문제들 정리

1. String은 기본적으로 불변이다. - 처음에 직접 index로 접근하여 문자열을 조작하려고 시도했다.

2. String을 for문 반복시 각 문자는 Char자료형이다.

3. toInt() 메서드는 Char 자료형에 대해 ASCII 코드를 반환한다.
- '1'.toInt() 사용시 반환값은 49이다.
- '9'가 피연산자인 경우 범위 초과 문제가 일어날 수 있기 때문에 바람직한 연산이 아니다.
- ⚠️ "1".toInt()는 1이 됨 (String에 대해서는 올바르게 Int값을 반환)

4. toInt() 대안으로 digitToInt()를 사용할 수 있다.
- 단, char에서만 사용가능하다.
- '4'.digitToInt() = 4

5. 현재 코드를 보면 adjustList에서 ['4', '1']과 같은 list를
toString()으로 수행시 "41"로 생각했다. 하지만 결과는 "['4', '1']"이 된다.
- 이를 해결하기 위해 joinToString()을 사용할 수 있다.

val adjustString = adjustList.jointoString("") // 빈문자열로 배열의 요소 합치기
val adjustInt = adjustString.toInt()

지금 로직을 리부해 보니 40:03 prev -> 39:53과 같은 형태 처리가 되어있지 않다.

2차시도

javascript 코드 예제
                                    💡 로직을 수정

# case1. 1001 - 10 = 991을 정상적으로 처리하기
minute = 991 / 100
second = 991 & 100 = 91
- 91에서 51로 처리를 해줘야함
- if second > 60 then 9

# case2. 1051 + 10 = 1061을 정상 적으로 처리하기
minute = 1061 / 100 = 10 (expected : 11)
second = 1061 % 100 = 61 (expected : 01)

# 매번 갱신한다고 가정하면, case1의 경우 9n만, case2의 경우 6n의 경우만 처리해주면된다.
9n인 경우 9n - 40
6n인 경우 6n - 60 and minute + 1

+ format
String.format("%02d:%02d", minute, second)
javascript 코드 예제
                                    class Solution {

    fun seperateTime(time: String): Int {
        val intBuilder: StringBuilder = StringBuilder()
        intBuilder.append(time[0])
        intBuilder.append(time[1])
        intBuilder.append(time[3])
        intBuilder.append(time[4])

        return intBuilder.toString().toInt()
        // O(n)
    }

    fun solution(video_len: String, pos: String, otp_start: String, op_end: String, commands: Array<String>): String {
        val totalLen = seperateTime(video_len)
        var position = seperateTime(pos)
        val opStart = seperateTime(op_start)
        val opEnd = seperateTime(op_end)

        for (command in commands) {
            // chekc op
            if (opStart <= position && position <= opEnd) {
                position = opEnd
            }

            // do command
            if (command == "next") {
                position += 10

                if (position >= totalLen) {
                    position = totalLen
                    continue
                }

                var minute = position / 100
                var second = position % 100
                if (second >= 60) {
                    second = second - 60
                    minute++
                }
                position = 100 * minute + second

            } else {
                position -= 10
                if (position <= 0) {
                    position = 0
                    continue
                }

                var minute = position / 100
                var second = position % 100
                if (second >= 90) {
                    second = second - 40
                }
                position = 100 * minute + second
            }
        }

        // last check
        if (opStart <= position && position <= opEnd) {
            position = opEnd
        }

        return String.format("%02d:%02d", position / 100, position % 100)
    }
}

// 1. 매 동작마다 오프닝 위치인지 체크가 필요함 - 오프닝 위치라면 end로 갱신해야함
// 2. 시간 String을 쪼개어서 분, 초로 나눠야 함
// 3. prev, next는 별거없음
// 4. 마지막 출력은 00:00 형태로 전환

// 4505 -10
// 4455 10 / 6의 나머지 + 1
//
javascript 코드 예제
                                    ⌛ 시간 복잡도 분석
1. seperateTime: O(1) // 항상 상수 - 5개의 문자로된 문자열
2. Command 내부 - O(commands) - command의 갯수에 따라 달라짐

* 구분을 쉽게 하기 위해 branch에 모두 따로 minute, second를 선언함

실패 경계값은 항상 마지막에 확인하자.

test case 6번에서 실패가 발생했다. 도저히 생각이 나지 않아 o1에게 물어봤더니 실패 케이스를 알려줬다.

javascript 코드 예제
                                    반례) 동영상 길이 "01:00"
      초기 위치 "00:59"
      오프닝 구간 don't care
      command ["next"]

동작
tatalLen = 100
position = 59
command == next true
|_
   position + 10 = 69
   if (position > totalLen) : false -> 사실은 true
   adjust position
   position = 109 > totalLen -- 여기서 확인해야하는데 확인을 안하고 넘어간다.

경계값을 체크하는 코드를 for문의 마지막 줄에 넣어줬더니 통과했다.