|
| 1 | +package leetcode_study |
| 2 | + |
| 3 | +/* |
| 4 | +* 디코딩할 수 있는 경우의 수를 구하는 문제 |
| 5 | +* 아래 풀이는 구할 수 있는 모든 경우의 수를 구한 뒤 예외 상황을 제외해 답을 구하는 풀이 |
| 6 | +* 주어진 문자열을 재귀를 통해 나눌 때, 중복의 경우를 모두 계산하기 때문에 "Memory Limit Exceeded" 발생 |
| 7 | +* 예를 들어, 길이가 50인 문자열에 대해 2^{50} 에 가까운 경우의 수가 생성될 수 있음. |
| 8 | +* */ |
| 9 | +fun numDecodings(s: String): Int { |
| 10 | + val alphabetList = mutableListOf<String>() |
| 11 | + var answer = 0 |
| 12 | + for (i in 1..26) { |
| 13 | + alphabetList.add(i.toString()) |
| 14 | + } |
| 15 | + val dividedList = lengthDivider(s) |
| 16 | + |
| 17 | + for (dividedElement in dividedList) { |
| 18 | + var index = 0 |
| 19 | + var value = 0 |
| 20 | + for (each in dividedElement) { |
| 21 | + val subString = s.substring(index, index + each) |
| 22 | + if (subString.first() != '0' && alphabetList.contains(subString)) { |
| 23 | + index += each |
| 24 | + value += 1 |
| 25 | + } |
| 26 | + } |
| 27 | + if (value == dividedElement.size) { |
| 28 | + answer += 1 |
| 29 | + } |
| 30 | + } |
| 31 | + return answer |
| 32 | +} |
| 33 | + |
| 34 | +fun lengthDivider(s: String): List<List<Int>> { |
| 35 | + val result = mutableListOf<List<Int>>() |
| 36 | + |
| 37 | + fun divide(target: Int, current: List<Int>) { |
| 38 | + if (target == 0) { |
| 39 | + result.add(current) |
| 40 | + return |
| 41 | + } |
| 42 | + if (target < 0) return |
| 43 | + |
| 44 | + divide(target - 1, current + 1) |
| 45 | + divide(target - 2, current + 2) |
| 46 | + } |
| 47 | + |
| 48 | + divide(s.length, emptyList()) |
| 49 | + return result |
| 50 | +} |
| 51 | + |
| 52 | +/* |
| 53 | +* 메모이제이션을 사용한 문제 해결 |
| 54 | +* 시간 복잡도: O(n) |
| 55 | +* -> 주어진 문자열의 길이 만큼 순회 |
| 56 | +* 공간 복잡도: O(n) |
| 57 | +* -> 주어진 길이 만큼 기존 정보를 '메모'할 수 있는 공간 |
| 58 | +* */ |
| 59 | +fun numDecodings(s: String): Int { |
| 60 | + if (s.isEmpty() || s.first() == '0') return 0 // 빈 문자열 또는 '0'으로 시작하는 경우 디코딩 불가 |
| 61 | + |
| 62 | + val n = s.length |
| 63 | + val dp = IntArray(n + 1) // dp[i]는 s[0..i-1]까지 디코딩 가능한 경우의 수를 저장 |
| 64 | + dp[0] = 1 // 빈 문자열은 하나의 방법으로 디코딩 가능 |
| 65 | + dp[1] = if (s[0] != '0') 1 else 0 // 첫 문자가 '0'이 아니면 한 가지 방법 가능 |
| 66 | + |
| 67 | + for (i in 2..n) { |
| 68 | + val oneDigit = s.substring(i - 1, i).toInt() |
| 69 | + val twoDigits = s.substring(i - 2, i).toInt() |
| 70 | + |
| 71 | + // 한 자리 숫자가 유효한 경우 |
| 72 | + if (oneDigit in 1..9) { |
| 73 | + dp[i] += dp[i - 1] |
| 74 | + } |
| 75 | + |
| 76 | + // 두 자리 숫자가 유효한 경우 |
| 77 | + if (twoDigits in 10..26) { |
| 78 | + dp[i] += dp[i - 2] |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + return dp[n] |
| 83 | +} |
0 commit comments