Skip to content

Commit 357197c

Browse files
authored
Merge pull request ByteByteGoHq#43 from marttp/kotlin-dynamic-programming
Kotlin - Chapter 15: Dynamic Programming
2 parents e0aeec3 + 7be8452 commit 357197c

20 files changed

+456
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun climbingStairsBottomUp(n: Int): Int {
2+
if (n <= 2) {
3+
return n
4+
}
5+
val dp = IntArray(n + 1)
6+
// Base cases.
7+
dp[1] = 1
8+
dp[2] = 2
9+
// Starting from step 3, calculate the number of ways to reach each
10+
// step until the n-th step.
11+
for (i in 3..n) {
12+
dp[i] = dp[i - 1] + dp[i - 2]
13+
}
14+
return dp[n]
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fun climbingStairsBottomUpOptimized(n: Int): Int {
2+
if (n <= 2) {
3+
return n
4+
}
5+
// Set 'oneStepBefore' and 'twoStepsBefore' as the base cases.
6+
var oneStepBefore = 2
7+
var twoStepsBefore = 1
8+
for (i in 3..n) {
9+
// Calculate the number of ways to reach the current step.
10+
val current = oneStepBefore + twoStepsBefore
11+
// Update the values for the next iteration.
12+
twoStepsBefore = oneStepBefore
13+
oneStepBefore = current
14+
}
15+
return oneStepBefore
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fun climbingStairsTopDown(n: Int): Int {
2+
val memo = hashMapOf<Int, Int>()
3+
return climbingStairsTopDownHelper(n, memo)
4+
}
5+
6+
fun climbingStairsTopDownHelper(n: Int, memo: HashMap<Int, Int>): Int {
7+
// Base cases: With a 1-step staircase, there's only one way to
8+
// climb it. With a 2-step staircase, there are two ways to climb it.
9+
if (n <= 2) {
10+
return n
11+
}
12+
if (memo[n] != 0) {
13+
return memo[n]!!
14+
}
15+
// The number of ways to climb to the n-th step is equal to the sum
16+
// of the number of ways to climb to step n - 1 and to n - 2.
17+
memo[n] = climbingStairsTopDownHelper(n - 1, memo) + climbingStairsTopDownHelper(n - 2, memo)
18+
return memo[n]!!
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
fun knapsack(cap: Int, weights: List<Int>, values: List<Int>): Int {
2+
val n = values.size
3+
// Base case: Set the first column and last row to 0 by
4+
// initializing the entire DP table to 0.
5+
val dp = Array(n + 1) { IntArray(cap + 1) }
6+
// Populate the DP table.
7+
for (i in n - 1 downTo 0) {
8+
for (c in 1..cap) {
9+
// If the item 'i' fits in the current knapsack capacity,
10+
// the maximum value at 'dp[i][c]' is the largest of either:
11+
// 1. The maximum value if we include item 'i'.
12+
// 2. The maximum value if we exclude item 'i'.
13+
if (weights[i] <= c) {
14+
dp[i][c] = Math.max(values[i] + dp[i + 1][c - weights[i]], dp[i + 1][c])
15+
} else {
16+
// If it doesn't fit, we have to exclude it.
17+
dp[i][c] = dp[i + 1][c]
18+
}
19+
}
20+
}
21+
return dp[0][cap]
22+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
fun knapsackOptimized(cap: Int, weights: List<Int>, values: List<Int>): Int {
2+
val n = values.size
3+
// Initialize 'prevRow' as the DP values of the row below the
4+
// current row.
5+
var prevRow = IntArray(cap + 1)
6+
for (i in n - 1 downTo 0) {
7+
// Set the first cell of the 'currRow' to 0 to set the base
8+
// case for this row. This is done by initializing the entire
9+
// row to 0.
10+
val currRow = IntArray(cap + 1)
11+
for (c in 1..cap) {
12+
// If item 'i' fits in the current knapsack capacity, the
13+
// maximum value at 'currRow[c]' is the largest of either:
14+
// 1. The maximum value if we include item 'i'.
15+
// 2. The maximum value if we exclude item 'i'.
16+
if (weights[i] <= c) {
17+
currRow[c] = Math.max(values[i] + prevRow[c - weights[i]], prevRow[c])
18+
} else {
19+
// If it doesn't fit, we have to exclude it.
20+
currRow[c] = prevRow[c]
21+
}
22+
}
23+
// Set 'prevRow' to 'currRow' values for the next iteration.
24+
prevRow = currRow
25+
}
26+
return prevRow[cap]
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
fun largestSquareInAMatrix(matrix: List<List<Int>>): Int {
2+
if (matrix.isEmpty()) {
3+
return 0
4+
}
5+
val m = matrix.size
6+
val n = matrix[0].size
7+
val dp = Array(m) { IntArray(n) }
8+
var maxLen = 0
9+
// Base case: If a cell in row 0 is 1, the largest square ending there has a
10+
// length of 1.
11+
for (j in 0 until n) {
12+
if (matrix[0][j] == 1) {
13+
dp[0][j] = 1
14+
maxLen = 1
15+
}
16+
}
17+
// Base case: If a cell in column 0 is 1, the largest square ending there has
18+
// a length of 1.
19+
for (i in 0 until m) {
20+
if (matrix[i][0] == 1) {
21+
dp[i][0] = 1
22+
maxLen = 1
23+
}
24+
}
25+
// Populate the rest of the DP table.
26+
for (i in 1 until m) {
27+
for (j in 1 until n) {
28+
if (matrix[i][j] == 1) {
29+
// The length of the largest square ending here is determined by
30+
// the smallest square ending at the neighboring cells (left,
31+
// top-left, top), plus 1 to include this cell.
32+
dp[i][j] = 1 + minOf(dp[i - 1][j], dp[i - 1][j - 1], dp[i][j - 1])
33+
}
34+
maxLen = maxOf(maxLen, dp[i][j])
35+
}
36+
}
37+
return maxLen * maxLen
38+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
fun largestSquareInAMatrixOptimized(matrix: Array<IntArray>): Int {
2+
if (matrix.isEmpty()) return 0
3+
val m = matrix.size
4+
val n = matrix[0].size
5+
var prevRow = IntArray(n)
6+
var maxLen = 0
7+
// Iterate through the matrix.
8+
for (i in 0 until m) {
9+
val currRow = IntArray(n)
10+
for (j in 0 until n) {
11+
// Base cases: if we’re in row 0 or column 0, the largest square ending
12+
// here has a length of 1. This can be set by using the value in the
13+
// input matrix.
14+
if (i == 0 || j == 0) {
15+
currRow[j] = matrix[i][j]
16+
} else {
17+
if (matrix[i][j] == 1) {
18+
// curr_row[j] = 1 + min(left, top-left, top)
19+
currRow[j] = 1 + minOf(currRow[j - 1], prevRow[j - 1], prevRow[j])
20+
}
21+
}
22+
maxLen = maxOf(maxLen, currRow[j])
23+
}
24+
// Update 'prevRow' with 'currRow' values for the next iteration.
25+
prevRow = currRow.copyOf()
26+
}
27+
return maxLen * maxLen
28+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
fun longestCommonSubsequence(s1: String, s2: String): Int {
2+
// Base case: Set the last row and last column to 0 by
3+
// initializing the entire DP table with 0s.
4+
val dp = Array(s1.length + 1) { IntArray(s2.length + 1) }
5+
// Populate the DP table.
6+
for (i in s1.length - 1 downTo 0) {
7+
for (j in s2.length - 1 downTo 0) {
8+
// If the characters match, the length of the LCS at
9+
// 'dp[i][j]' is 1 + the LCS length of the remaining
10+
// substrings.
11+
if (s1[i] == s2[j]) {
12+
dp[i][j] = 1 + dp[i + 1][j + 1]
13+
} else {
14+
// If the characters don't match, the LCS length at
15+
// 'dp[i][j]' can be found by either:
16+
// 1. Excluding the current character of s1.
17+
// 2. Excluding the current character of s2.
18+
dp[i][j] = maxOf(dp[i + 1][j], dp[i][j + 1])
19+
}
20+
}
21+
}
22+
return dp[0][0]
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
fun longestCommonSubsequenceOptimized(s1: String, s2: String): Int {
2+
// Initialize 'prevRow' as the DP values of the last row.
3+
var prevRow = IntArray(s2.length + 1)
4+
for (i in s1.length - 1 downTo 0) {
5+
// Set the last cell of 'currRow' to 0 to set the base case for
6+
// this row. This is done by initializing the entire row to 0.
7+
val currRow = IntArray(s2.length + 1)
8+
for (j in s2.length - 1 downTo 0) {
9+
// If the characters match, the length of the LCS at
10+
// 'currRow[j]' is 1 + the LCS length of the remaining
11+
// substrings ('prevRow[j + 1]').
12+
if (s1[i] == s2[j]) {
13+
currRow[j] = 1 + prevRow[j + 1]
14+
} else {
15+
// If the characters don't match, the LCS length at
16+
// 'currRow[j]' can be found by either:
17+
// 1. Excluding the current character of s1 ('prevRow[j]').
18+
// 2. Excluding the current character of s2 ('currRow[j + 1]').
19+
currRow[j] = maxOf(prevRow[j], currRow[j + 1])
20+
}
21+
}
22+
// Update 'prevRow' with 'currRow' values for the next iteration.
23+
prevRow = currRow
24+
}
25+
return prevRow[0]
26+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
fun longestPalindromeInAString(s: String): String {
2+
val n = s.length
3+
if (n == 0) {
4+
return ""
5+
}
6+
val dp = Array(n) { BooleanArray(n) }
7+
var maxLen = 1
8+
var startIndex = 0
9+
// Base case: a single character is always a palindrome.
10+
for (i in 0 until n) {
11+
dp[i][i] = true
12+
}
13+
// Base case: a substring of length two is a palindrome if both
14+
// characters are the same.
15+
for (i in 0 until n - 1) {
16+
if (s[i] == s[i + 1]) {
17+
dp[i][i + 1] = true
18+
maxLen = 2
19+
startIndex = i
20+
}
21+
}
22+
// Find palindromic substrings of length 3 or greater.
23+
for (substringLen in 3..n) {
24+
// Iterate through each substring of length 'substringLen'.
25+
for (i in 0 until n - substringLen + 1) {
26+
val j = i + substringLen - 1
27+
// If the first and last characters are the same, and the
28+
// inner substring is a palindrome, then the current
29+
// substring is a palindrome.
30+
if (s[i] == s[j] && dp[i + 1][j - 1]) {
31+
dp[i][j] = true
32+
maxLen = substringLen
33+
startIndex = i
34+
}
35+
}
36+
}
37+
return s.substring(startIndex, startIndex + maxLen)
38+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
fun longestPalindromeInAStringExpanding(s: String): String {
2+
val n = s.length
3+
var start = 0
4+
var maxLen = 0
5+
for (center in 0 until n) {
6+
// Check for odd-length palindromes.
7+
val (oddStart, oddLength) = expandPalindrome(center, center, s)
8+
if (oddLength > maxLen) {
9+
start = oddStart
10+
maxLen = oddLength
11+
}
12+
// Check for even-length palindromes.
13+
if (center < n - 1 && s[center] == s[center + 1]) {
14+
val (evenStart, evenLength) = expandPalindrome(center, center + 1, s)
15+
if (evenLength > maxLen) {
16+
start = evenStart
17+
maxLen = evenLength
18+
}
19+
}
20+
}
21+
return s.substring(start, start + maxLen)
22+
}
23+
24+
// Expands outward from the center of a base case to identify the start
25+
// index and length of the longest palindrome that extends from this
26+
// base case.
27+
fun expandPalindrome(left: Int, right: Int, s: String): Pair<Int, Int> {
28+
var l = left
29+
var r = right
30+
while (l > 0 && r < s.length - 1 && s[l - 1] == s[r + 1]) {
31+
l--
32+
r++
33+
}
34+
return l to r - l + 1
35+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fun matrixPathways(m: Int, n: Int): Int {
2+
// Base cases: Set all cells in row 0 and column 0 to 1. We can
3+
// do this by initializing all cells in the DP table to 1.
4+
val dp = Array(m) { IntArray(n) { 1 } }
5+
// Fill in the rest of the DP table.
6+
for (r in 1 until m) {
7+
for (c in 1 until n) {
8+
// Paths to current cell = paths from above + paths from
9+
// left.
10+
dp[r][c] = dp[r - 1][c] + dp[r][c - 1]
11+
}
12+
}
13+
return dp[m - 1][n - 1]
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
fun matrixPathwaysOptimized(m: Int, n: Int): Int {
2+
// Initialize 'prevRow' as the DP values of row 0, which are all 1s.
3+
var prevRow = IntArray(n) { 1 }
4+
// Iterate through the matrix starting from row 1.
5+
for (r in 1 until m) {
6+
// Set the first cell of 'currRow' to 1. This is done by
7+
// setting the entire row to 1.
8+
val currRow = IntArray(n) { 1 }
9+
for (c in 1 until n) {
10+
// The number of unique paths to the current cell is the sum
11+
// of the paths from the cell above it ('prevRow[c]') and
12+
// the cell to the left ('currRow[c - 1]').
13+
currRow[c] = prevRow[c] + currRow[c - 1]
14+
}
15+
// Update 'prevRow' with 'currRow' values for the next
16+
// iteration.
17+
prevRow = currRow
18+
}
19+
// The last element in 'prevRow' stores the result for the
20+
// bottom-right cell.
21+
return prevRow[n - 1]
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fun maximumSubarraySum(nums: List<Int>): Int {
2+
if (nums.isEmpty()) {
3+
return 0
4+
}
5+
// Set the sum variables to negative infinity to ensure negative
6+
// sums can be considered.
7+
var maxSum = Int.MIN_VALUE
8+
var currentSum = Int.MIN_VALUE
9+
// Iterate through the array to find the maximum subarray sum.
10+
for (num in nums) {
11+
// Either add the current number to the existing running sum, or
12+
// start a new subarray at the current number.
13+
currentSum = maxOf(currentSum + num, num)
14+
maxSum = maxOf(maxSum, currentSum)
15+
}
16+
return maxSum
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fun maximumSubarraySumDP(nums: IntArray): Int {
2+
val n = nums.size
3+
if (n == 0) {
4+
return 0
5+
}
6+
val dp = IntArray(n)
7+
// Base case: the maximum subarray sum of an array with just one
8+
// element is that element.
9+
dp[0] = nums[0]
10+
var maxSum = dp[0]
11+
// Populate the rest of the DP array.
12+
for (i in 1 until n) {
13+
// Determine the maximum subarray sum ending at the current
14+
// index.
15+
dp[i] = maxOf(dp[i - 1] + nums[i], nums[i])
16+
maxSum = maxOf(maxSum, dp[i])
17+
}
18+
return maxSum
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fun maximumSubarraySumDPOptimized(nums: IntArray): Int {
2+
val n = nums.size
3+
if (n == 0) {
4+
return 0
5+
}
6+
var currentSum = nums[0]
7+
var maxSum = nums[0]
8+
for (i in 1 until n) {
9+
currentSum = maxOf(nums[i], currentSum + nums[i])
10+
maxSum = maxOf(maxSum, currentSum)
11+
}
12+
return maxSum
13+
}

0 commit comments

Comments
 (0)