├── 01-01-knapsack.rb ├── 02-unbounded-knapsack.rb ├── 03-fibonacci-numbers.rb ├── 04-palindromic-substrings.rb ├── 05-longest-common-substring.rb └── README.md /01-01-knapsack.rb: -------------------------------------------------------------------------------- 1 | puts "Knapsack" 2 | 3 | def knapsack(w, p, cap, len = w.length) 4 | return 0 if len.zero? || cap <= 0 5 | [knapsack(w, p, cap, len - 1), p[len - 1] + knapsack(w, p, cap - w[len - 1], len - 1)].max 6 | end 7 | 8 | p knapsack([1, 2, 3, 4], [5, 6, 7, 8], 4, 4) 9 | 10 | def knapsack_dp(w, p, cap) 11 | prev = (cap + 1).times.collect { 0 } 12 | cur = (cap + 1).times.collect { 0 } 13 | 1.upto(w.length) do |len| 14 | 0.upto(cap) do |cap| 15 | if w[len - 1] <= cap 16 | cur[cap] = [prev[cap], p[len - 1] + prev[cap - w[len - 1]]].max 17 | else 18 | cur[cap] = prev[cap] 19 | end 20 | end 21 | cur, prev = prev, cur 22 | end 23 | prev[cap] 24 | end 25 | 26 | p knapsack([1, 2, 3, 4], [5, 6, 7, 8], 4, 4) 27 | 28 | puts "Equal subsets" 29 | 30 | def can_partition(nums) 31 | sum = nums.sum 32 | return false if sum.odd? 33 | can_partition_dp(nums, sum / 2) 34 | end 35 | 36 | def can_partition_r(nums, target, len = nums.length) 37 | return false if target < 0 38 | return target.zero? if len.zero? 39 | can_partition_r(nums, target, len - 1) || can_partition_r(nums, target - nums[len - 1], len - 1) 40 | end 41 | 42 | def can_partition_dp(nums, target, len = nums.length) 43 | prev = (1 + target).times.collect { false } 44 | cur = (1 + target).times.collect { false } 45 | prev[0] = true 46 | 1.upto(nums.length) do |len| 47 | 0.upto(target) do |target| 48 | cur[target] = prev[target] || (nums[len - 1] <= target && prev[target - nums[len - 1]]) 49 | end 50 | prev, cur = cur, prev 51 | end 52 | prev[target] 53 | end 54 | 55 | p can_partition [1, 2, 3, 6] 56 | p can_partition [1, 2, 3, 5] 57 | 58 | puts "Min subset difference" 59 | 60 | def min_subset_diff(nums) 61 | sum = nums.sum 62 | found = can_partition_dp(nums, sum / 2) 63 | sum - 2 * found 64 | end 65 | 66 | def can_partition_dp(nums, target, len = nums.length) 67 | prev = (1 + target).times.collect { false } 68 | cur = (1 + target).times.collect { false } 69 | prev[0] = true 70 | 1.upto(nums.length) do |len| 71 | 0.upto(target) do |target| 72 | cur[target] = prev[target] || (nums[len - 1] <= target && prev[target - nums[len - 1]]) 73 | end 74 | prev, cur = cur, prev 75 | end 76 | target.downto(0).find { |x| prev[x] } 77 | end 78 | 79 | p min_subset_diff [1, 2, 3, 5] 80 | p min_subset_diff [1, 2, 3, 6] 81 | p min_subset_diff [1, 2, 3, 600] 82 | 83 | puts "Count of subset sum" 84 | 85 | def subset_count(nums, target, len = nums.length) 86 | prev = (1 + target).times.collect { 0 } 87 | cur = (1 + target).times.collect { 0 } 88 | prev[0] = 1 89 | 1.upto(nums.length) do |len| 90 | 0.upto(target) do |target| 91 | cur[target] = prev[target] 92 | cur[target] += prev[target - nums[len - 1]] if nums[len - 1] <= target 93 | end 94 | prev, cur = cur, prev 95 | end 96 | prev[target] 97 | end 98 | 99 | p subset_count [1, 2, 2, 3, 4], 4 100 | 101 | puts "Target sum" 102 | 103 | def count_target_sum(nums, target, len = nums.length) 104 | return 0 if len < 1 105 | target_sum(nums, target - nums[len - 1], len - 1) + target_sum(nums, target + nums[len - 1], len - 1) 106 | end 107 | 108 | def count_target_sum(nums, target) 109 | target = (nums.sum + target) / 2 110 | prev = (1 + target).times.collect { 0 } 111 | cur = (1 + target).times.collect { 0 } 112 | prev[0] = 1 113 | 1.upto(nums.length) do |len| 114 | 0.upto(target) do |target| 115 | cur[target] = prev[target] 116 | cur[target] += prev[target - nums[len - 1]] if nums[len - 1] <= target 117 | end 118 | prev, cur = cur, prev 119 | end 120 | prev[target] 121 | end 122 | 123 | # P - S = T 124 | # P + S = SUM 125 | p count_target_sum [1, 2, 2, 3], 0 126 | -------------------------------------------------------------------------------- /02-unbounded-knapsack.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def solve_knapsack(profits, weights, capacity); end 4 | 5 | def solve_knapsack_r(profits, weights, capacity, len = profits.length) 6 | return 0 if len.zero? 7 | 8 | if weights[len - 1] <= capacity 9 | [solve_knapsack_r(profits, weights, capacity, len - 1), 10 | profits[len - 1] + solve_knapsack_r(profits, weights, capacity - weights[len - 1], len)].max 11 | else 12 | solve_knapsack_r(profits, weights, capacity, len - 1) 13 | end 14 | end 15 | 16 | def solve_knapsack(profits, weights, capacity) 17 | prev = (capacity + 1).times.collect { 0 } 18 | current = (capacity + 1).times.collect { 0 } 19 | 1.upto(profits.length) do |len| 20 | 0.upto(capacity) do |c| 21 | current[c] = if c >= weights[len - 1] 22 | [prev[c], profits[len - 1] + current[c - weights[len - 1]]].max 23 | else 24 | prev[c] 25 | end 26 | end 27 | prev, current = current, prev 28 | end 29 | prev[capacity] 30 | end 31 | 32 | def solve_knapsack_with_items(profits, weights, capacity) 33 | dp = (profits.length + 1).times.collect { (capacity + 1).times.collect { 0 } } 34 | 1.upto(profits.length) do |len| 35 | 0.upto(capacity) do |c| 36 | if c >= weights[len - 1] 37 | dp[len][c] = [dp[len - 1][c], profits[len - 1] + dp[len][c - weights[len - 1]]].max 38 | else 39 | dp[len][c] = dp[len - 1][c] 40 | end 41 | end 42 | end 43 | get_items(dp, profits, weights, capacity) 44 | end 45 | 46 | def get_items(dp, profits, weights, capacity, len = profits.length) 47 | res = [] 48 | while len > 0 49 | count = 0 50 | while dp[len - 1][capacity] != dp[len][capacity] 51 | count += 1 52 | capacity -= weights[len - 1] 53 | end 54 | res << [count, profits[len - 1]] if count > 0 55 | len -= 1 56 | end 57 | res 58 | end 59 | 60 | p solve_knapsack([15, 50, 60, 90], [1, 3, 4, 5], 8) 61 | p solve_knapsack([15, 50, 60, 90], [1, 3, 4, 5], 6) 62 | 63 | p solve_knapsack_r([15, 50, 60, 90], [1, 3, 4, 5], 8) 64 | p solve_knapsack_r([15, 50, 60, 90], [1, 3, 4, 5], 6) 65 | 66 | p solve_knapsack_with_items([15, 50, 60, 90], [1, 3, 4, 5], 8) 67 | p solve_knapsack_with_items([15, 50, 60, 90], [1, 3, 4, 5], 6) 68 | 69 | puts 'ROD CUTTING' 70 | def rod_cutting_r(lengths, prices, l, len = prices.length) 71 | return 0 if len.zero? 72 | 73 | if l >= lengths[len - 1] 74 | [rod_cutting_r(lengths, prices, l, len - 1), 75 | prices[len - 1] + rod_cutting_r(lengths, prices, l - lengths[len - 1], len)].max 76 | else 77 | rod_cutting_r(lengths, prices, l, len - 1) 78 | end 79 | end 80 | 81 | def rod_cutting_dp(lengths, prices, l) 82 | prev = (l + 1).times.collect { 0 } 83 | current = (l + 1).times.collect { 0 } 84 | 1.upto(prices.length) do |len| 85 | 0.upto(l) do |l| 86 | current[l] = if l >= lengths[len - 1] 87 | [prev[l], prices[len - 1] + current[l - lengths[len - 1]]].max 88 | else 89 | prev[l] 90 | end 91 | end 92 | prev, current = current, prev 93 | end 94 | prev[l] 95 | end 96 | 97 | def rod_cutting_items(lengths, prices, l) 98 | dp = (prices.length + 1).times.collect { (l + 1).times.collect { 0 } } 99 | 1.upto(prices.length) do |len| 100 | 0.upto(l) do |l| 101 | dp[len][l] = if l >= lengths[len - 1] 102 | [dp[len - 1][l], prices[len - 1] + dp[len][l - lengths[len - 1]]].max 103 | else 104 | dp[len - 1][l] 105 | end 106 | end 107 | end 108 | res = [] 109 | len = prices.length 110 | while len > 0 111 | count = 0 112 | while dp[len][l] != dp[len - 1][l] 113 | l -= lengths[len - 1] 114 | count += 1 115 | end 116 | res << [count, lengths[len - 1]] if count > 0 117 | len -= 1 118 | end 119 | res 120 | end 121 | 122 | p rod_cutting_dp [1, 2, 3, 4, 5], [2, 6, 7, 10, 13], 5 123 | 124 | p rod_cutting_items [1, 2, 3, 4, 5], [2, 6, 7, 10, 13], 5 125 | 126 | puts 'COIN CHANGE' 127 | 128 | def count_change(denominations, total, len = denominations.length) 129 | return total.zero? ? 1 : 0 if len.zero? 130 | 131 | if denominations[len - 1] <= total 132 | count_change(denominations, total, len - 1) + count_change(denominations, total - denominations[len - 1], len) 133 | else 134 | count_change(denominations, total, len - 1) 135 | end 136 | end 137 | 138 | def count_change(denominations, total) 139 | prev = (1 + total).times.collect { 0 } 140 | prev[0] = 1 141 | cur = (1 + total).times.collect { 0 } 142 | 1.upto(denominations.length) do |len| 143 | 0.upto(total) do |total| 144 | cur[total] = if total >= denominations[len - 1] 145 | prev[total] + cur[total - denominations[len - 1]] 146 | else 147 | prev[total] 148 | end 149 | end 150 | prev, cur = cur, prev 151 | end 152 | prev[total] 153 | end 154 | 155 | def count_change(denominations, total) 156 | prev = (1 + total).times.collect { 0 } 157 | prev[0] = 1 158 | cur = (1 + total).times.collect { 0 } 159 | 1.upto(denominations.length) do |len| 160 | 0.upto(total) do |total| 161 | cur[total] = if total >= denominations[len - 1] 162 | prev[total] + cur[total - denominations[len - 1]] 163 | else 164 | prev[total] 165 | end 166 | end 167 | prev, cur = cur, prev 168 | end 169 | prev[total] 170 | end 171 | p count_change [1, 2, 3], 5 172 | 173 | puts 'MIN COIN CHANGE' 174 | 175 | def min_change(denominations, total, len = denominations.length) 176 | return 1.0 / 0 if total < 0 177 | return total.zero? ? 0 : 1.0 / 0 if len <= 0 178 | 179 | [min_change(denominations, total, len - 1), 1 + min_change(denominations, total - denominations[len - 1], len)].min 180 | end 181 | 182 | def min_change(denominations, total, _len = denominations.length) 183 | prev = (total + 1).times.collect { 1.0 / 0 } 184 | prev[0] = 0 185 | cur = (total + 1).times.collect { 1.0 / 0 } 186 | 1.upto(denominations.length) do |len| 187 | 0.upto(total) do |total| 188 | cur[total] = if total >= denominations[len - 1] 189 | [prev[total], 1 + cur[total - denominations[len - 1]]].min 190 | else 191 | prev[total] 192 | end 193 | end 194 | prev, cur = cur, prev 195 | end 196 | prev[total] 197 | end 198 | 199 | def min_change(denominations, total, _len = denominations.length) 200 | max = denominations.max + 1 201 | dp = max.times.collect { 0 } 202 | 1.upto(total) do |amount| 203 | dp[amount % max] = 1.0 / 0 204 | denominations.each do |coin| 205 | if coin <= amount 206 | dp[amount % max] = 1 + dp[(amount - coin) % max] if dp[(amount - coin) % max] + 1 < dp[amount % max] 207 | end 208 | end 209 | end 210 | dp[total % max] 211 | end 212 | 213 | p min_change [1, 2, 3], 5 214 | p min_change [1, 2, 3], 7 215 | 216 | puts "Ribbon Cut" 217 | 218 | def ribbon_cut(lengths, size, len = lengths.length) 219 | return 0 if len.zero? 220 | if size >= lengths[len - 1] 221 | [1 + ribbon_cut(lengths, size - lengths[len - 1], len), ribbon_cut(lengths, size, len - 1)].max 222 | else 223 | ribbon_cut(lengths, size, len - 1) 224 | end 225 | end 226 | 227 | p ribbon_cut [2,3,5], 5 228 | 229 | def ribbon_cut(lengths, size, len = lengths.length) 230 | prev = (size + 1).times.collect { 0 } 231 | cur = (size + 1).times.collect { 0 } 232 | 1.upto(lengths.length) do |len| 233 | 1.upto(size) do |size| 234 | cur[size] = if size >= lengths[len - 1] 235 | [prev[size], 1 + cur[size - lengths[len - 1]]].max 236 | else 237 | prev[size] 238 | end 239 | end 240 | prev, cur = cur, prev 241 | end 242 | prev[size] 243 | end 244 | 245 | p ribbon_cut [2,3,5], 5 246 | 247 | 248 | def ribbon_cut(lengths, size, len = lengths.length) 249 | max = lengths.max + 1 250 | dp = max.times.collect { 0 } 251 | max_cut = (size + 1).times.collect { 0 } 252 | 1.upto(size) do |size| 253 | dp[size % max] = 0 254 | lengths.each do |len| 255 | if size >= len && 1 + dp[(size - len) % max] >= dp[size % max] 256 | dp[size % max] = 1 + dp[(size - len) % max] 257 | max_cut[size] = len 258 | end 259 | end 260 | end 261 | res = [] 262 | while size > 0 263 | res.unshift(max_cut[size]) 264 | size -= max_cut[size] 265 | end 266 | res 267 | end 268 | 269 | p ribbon_cut [2,3,5], 5 270 | p ribbon_cut [2,3,5], 8 -------------------------------------------------------------------------------- /03-fibonacci-numbers.rb: -------------------------------------------------------------------------------- 1 | puts "Fibonacchi" 2 | 3 | def fib(n) 4 | return n if n < 2 5 | prev, cur = 0, 1 6 | while n > 1 7 | cur, prev = cur + prev, cur 8 | n -= 1 9 | end 10 | cur 11 | end 12 | 13 | p 0.upto(10).collect { |n| fib(n) } 14 | 15 | puts "Stair Case" 16 | 17 | def count_ways(stairs) 18 | return 1 if stairs.zero? 19 | total_steps = 0 20 | [1, 2, 3].each do |steps| 21 | total_steps += count_ways(stairs - steps) if stairs >= steps 22 | end 23 | total_steps 24 | end 25 | 26 | def count_ways_dp(stairs) 27 | dp = [1] 28 | 1.upto(stairs) do |stairs| 29 | if stairs >= 3 30 | dp[stairs] = dp[stairs - 1] + dp[stairs - 2] + dp[stairs - 3] 31 | elsif stairs >= 2 32 | dp[stairs] = dp[stairs - 1] + dp[stairs - 2] 33 | else 34 | dp[stairs] = dp[stairs - 1] 35 | end 36 | end 37 | dp[stairs] 38 | end 39 | 40 | def count_ways_dp(stairs, steps = [1, 2, 3]) 41 | max = steps.max + 1 42 | dp = (max + 1).times.collect { 0 } 43 | dp[0] = 1 44 | 1.upto(stairs) do |stairs| 45 | dp[stairs % max] = 0 46 | steps.each do |step| 47 | dp[stairs % max] += dp[(stairs - step) % max] if stairs >= step 48 | end 49 | end 50 | dp[stairs % max] 51 | end 52 | 53 | p count_ways_dp(4) 54 | 55 | puts "Num factors" 56 | 57 | def num_factors(num, numbers = [1, 3, 4]) 58 | return 1 if num.zero? 59 | numbers.collect do |factor| 60 | if num >= factor 61 | num_factors(num - factor, numbers = [1, 3, 4]) 62 | else 63 | 0 64 | end 65 | end.sum 66 | end 67 | 68 | p num_factors 4 69 | 70 | def num_factors_dp(num, numbers = [1, 3, 4]) 71 | max = numbers.max + 1 72 | dp = max.times.collect { 0 } 73 | dp[0] = 1 74 | 1.upto(num) do |num| 75 | dp[num % max] = 0 76 | factors.each do |factor| 77 | next if factor > num 78 | dp[num % max] += dp[(num - factor) % max] 79 | end 80 | end 81 | dp[num % max] 82 | end 83 | 84 | p num_factors 4 85 | 86 | puts "MIN JUMPS" 87 | 88 | def min_jumps(nums, idx = nums.length - 1) 89 | return 0 if idx == 0 90 | min = nums.length + 1 91 | (idx - 1).downto(0) do |other_idx| 92 | next if other_idx + nums[other_idx] != idx 93 | cost_from_other_idx = 1 + min_jumps(nums, other_idx) 94 | min = cost_from_other_idx if cost_from_other_idx < min 95 | end 96 | min 97 | end 98 | 99 | p min_jumps [2, 1, 1, 1, 4] 100 | p min_jumps [1, 1, 3, 6, 9, 3, 0, 1, 3] 101 | 102 | def min_jumps(nums) 103 | dp = nums.length.times.collect { 1.0 / 0 } 104 | dp[0] = 0 105 | 1.upto(nums.length - 1) do |idx| 106 | (idx - 1).downto(0) do |other_idx| 107 | next if other_idx + nums[other_idx] != idx 108 | dp[idx] = 1 + dp[other_idx] if dp[idx] < 0 || 1 + dp[other_idx] < dp[idx] 109 | end 110 | end 111 | dp[nums.length - 1] 112 | end 113 | 114 | p min_jumps [2, 1, 1, 1, 4] 115 | p min_jumps [1, 1, 3, 6, 9, 3, 0, 1, 3] 116 | 117 | puts "MIN JUMPS WITH FEE" 118 | 119 | def min_jumps_with_fee(fees, idx = fees.length) 120 | return fees[0] if idx == 1 121 | return 1.0 / 0 if idx < 0 122 | [1, 2, 3].collect do |steps| 123 | min_jumps_with_fee(fees, idx - steps) 124 | end.min + fees[idx - 1] 125 | end 126 | 127 | p min_jumps_with_fee [1, 2, 5, 2, 1, 2] 128 | p min_jumps_with_fee [2, 3, 4, 5] 129 | 130 | def min_jumps_with_fee_dp(fees) 131 | dp = [1.0 / 0] * (fees.length + 1) 132 | dp[0] = 0 133 | 1.upto(fees.length) do |idx| 134 | [1, 2, 3].each do |steps| 135 | if idx >= steps && dp[idx - steps] < dp[idx] 136 | dp[idx] = dp[idx - steps] 137 | end 138 | end 139 | dp[idx] += fees[idx - 1] 140 | end 141 | dp[fees.length] 142 | end 143 | 144 | p min_jumps_with_fee_dp [1, 2, 5, 2, 1, 2] 145 | p min_jumps_with_fee_dp [2, 3, 4, 5] 146 | 147 | puts "House thief" 148 | 149 | def rob(houses, len = houses.length) 150 | return 0 if len <= 0 151 | [houses[len - 1] + rob(houses, len - 2), rob(houses, len - 1)].max 152 | end 153 | 154 | p rob [2, 5, 1, 3, 6, 2, 4] 155 | 156 | def rob_dp(houses) 157 | prev_prev, prev = 0, 0 158 | 0.upto(houses.length - 1) do |idx| 159 | cur = [prev, prev_prev + houses[idx]].max 160 | prev, prev_prev = cur, prev 161 | end 162 | prev 163 | end 164 | 165 | p rob_dp [2, 5, 1, 3, 6, 2, 4] 166 | p rob_dp [2, 10, 14, 8, 1] 167 | -------------------------------------------------------------------------------- /04-palindromic-substrings.rb: -------------------------------------------------------------------------------- 1 | puts "longest palindromic subsequence" 2 | 3 | def longest_palindromic_subsequence(str, first = 0, last = str.length - 1) 4 | return str[first] if first == last 5 | return "" if first > last 6 | if str[first] == str[last] 7 | str[first] + longest_palindromic_subsequence(str, first + 1, last - 1) + str[last] 8 | else 9 | without_last = longest_palindromic_subsequence(str, first, last - 1) 10 | without_first = longest_palindromic_subsequence(str, first + 1, last) 11 | if without_first.length > without_last.length 12 | without_first 13 | else 14 | without_last 15 | end 16 | end 17 | end 18 | 19 | p longest_palindromic_subsequence "cddpd" 20 | 21 | def longest_palindromic_subsequence(str, first = 0, last = str.length - 1) 22 | dp = str.length.times.collect { [0] * str.length } 23 | 0.upto(str.length - 1) do |idx| 24 | dp[idx][idx] = 1 25 | end 26 | 2.upto(str.length) do |len| 27 | 0.upto(str.length - len) do |first| 28 | last = first + len - 1 29 | dp[first][last] = if str[first] == str[last] 30 | 2 + dp[first + 1][last - 1] 31 | else 32 | [dp[first + 1][last], dp[first][last - 1]].max 33 | end 34 | end 35 | end 36 | first, last = 0, str.length - 1 37 | prefix, suffix = "", "" 38 | while first <= last 39 | if str[first] == str[last] 40 | prefix << str[first] 41 | suffix = str[last] + suffix if last > first 42 | first += 1 43 | last -= 1 44 | elsif dp[first + 1][last] > dp[first][last - 1] 45 | first += 1 46 | else 47 | last -= 1 48 | end 49 | end 50 | prefix + suffix 51 | end 52 | 53 | p longest_palindromic_subsequence "cddpd" 54 | 55 | puts "longest palindromic string" 56 | 57 | def longest_palindromic_substring(str, first = 0, last = str.length - 1) 58 | return '' if first > last 59 | return str[first] if first == last 60 | if str[first] == str[last] && longest_palindromic_substring(str, first + 1, last - 1).length == last - first - 1 61 | return str[first..last] 62 | else 63 | without_first = longest_palindromic_substring(str, first + 1, last) 64 | without_last = longest_palindromic_substring(str, first, last - 1) 65 | without_first.length > without_last.length ? without_first : without_last 66 | end 67 | end 68 | 69 | p longest_palindromic_substring("abdbca") 70 | p longest_palindromic_substring("cddpd") 71 | p longest_palindromic_substring("pqr") 72 | 73 | def longest_palindromic_substring(str) 74 | dp = str.length.times.collect { [-1] * str.length } 75 | 0.upto(str.length - 1) do |idx| 76 | dp[idx][idx] = 1 77 | end 78 | 2.upto(str.length) do |len| 79 | 0.upto(str.length - len) do |first| 80 | last = first + len - 1 81 | dp[first][last] = if str[first] == str[last] && dp[first + 1][last - 1] == last - first - 1 82 | last - first + 1 83 | else 84 | [dp[first + 1][last], dp[first][last - 1]].max 85 | end 86 | end 87 | end 88 | first, last = 0, str.length - 1 89 | while dp[first][last] != last - first + 1 90 | if dp[first + 1][last] > dp[first][last - 1] 91 | first += 1 92 | else 93 | last -= 1 94 | end 95 | end 96 | str[first..last] 97 | end 98 | 99 | p longest_palindromic_substring("abdbca") 100 | p longest_palindromic_substring("cddpd") 101 | p longest_palindromic_substring("pqr") 102 | 103 | puts "count of palindromic substrings" 104 | def count_palindromic_substrings(str, first = 0, last = str.length - 1, count = 0) 105 | 106 | end 107 | 108 | 109 | def count_palindromic_substrings(str, first = 0, last = str.length - 1, count = 0) 110 | dp = str.length.times.collect { [false] * str.length } 111 | count = 0 112 | 1.upto(str.length) do |len| 113 | 0.upto(str.length - len) do |first| 114 | last = first + len - 1 115 | if str[first] == str[last] && (first + 1 >= last - 1 || dp[first + 1][last - 1]) 116 | dp[first][last] = true 117 | count += 1 118 | end 119 | end 120 | end 121 | count 122 | end 123 | 124 | p count_palindromic_substrings("abdbca") 125 | p count_palindromic_substrings("cddpd") 126 | p count_palindromic_substrings("pqr") 127 | p count_palindromic_substrings("qqq") 128 | 129 | 130 | puts "minimum deletions to make palindrome" 131 | 132 | def min_deletions(str, first = 0, last = str.length - 1) 133 | return 0 if first == last 134 | return 1.0 / 0 if first > last 135 | if str[first] == str[last] 136 | min_deletions(str, first + 1, last - 1) 137 | else 138 | [1 + min_deletions(str, first + 1, last), 139 | 1 + min_deletions(str, first, last - 1)].min 140 | end 141 | end 142 | p min_deletions("abdbca") 143 | p min_deletions("cddpd") 144 | p min_deletions("pqr") 145 | 146 | def min_deletions(str) 147 | dp = str.length.times.collect { [ 1.0 / 0] * str.length } 148 | 1.upto(str.length) do |len| 149 | 0.upto(str.length - len) do |first| 150 | last = first + len - 1 151 | dp[first][last] = if str[first] == str[last] && first >= last - 1 152 | 0 153 | elsif str[first] == str[last] 154 | dp[first + 1][last - 1] 155 | else 156 | 1 + [dp[first + 1][last], dp[first][last - 1]].min 157 | end 158 | end 159 | end 160 | dp[0][str.length - 1] 161 | end 162 | p min_deletions("abdbca") 163 | p min_deletions("cddpd") 164 | p min_deletions("pqr") 165 | 166 | 167 | puts "Palindromic partitioning" 168 | 169 | def min_cuts(str, first = 0, last = str.length - 1) 170 | return 0 if first >= last 171 | if str[first] == str[last] && min_cuts(str, first + 1, last - 1) == 0 172 | 0 173 | else 174 | 1 + [min_cuts(str, first + 1, last), min_cuts(str, first, last - 1)].min 175 | end 176 | end 177 | 178 | p min_cuts("abdbca") 179 | p min_cuts("cddpd") 180 | p min_cuts("pqr") 181 | p min_cuts("pp") 182 | 183 | def min_cuts(str) 184 | dp = str.length.times.collect { [0] * str.length } 185 | 2.upto(str.length) do |len| 186 | 0.upto(str.length - len) do |first| 187 | last = first + len - 1 188 | next if str[first] == str[last] && dp[first + 1][last - 1].zero? 189 | dp[first][last] = 1 + [dp[first + 1][last], dp[first][last - 1]].min 190 | end 191 | end 192 | dp[0][str.length - 1] 193 | end 194 | 195 | p min_cuts("abdbca") 196 | p min_cuts("cddpd") 197 | p min_cuts("pqr") 198 | p min_cuts("pp") 199 | p min_cuts("madam") 200 | 201 | # Santa Calara Behaviour Health 202 | # 408 851 4890 203 | 204 | # 50,000 stock options 205 | 206 | # 73 million shares 207 | # 0.07 % 208 | 209 | # 2016 - 15million 210 | # 2017 - 50million 211 | # 2018 - 100million 212 | # 2019 - 170million 213 | -------------------------------------------------------------------------------- /05-longest-common-substring.rb: -------------------------------------------------------------------------------- 1 | puts "Longest common substring" 2 | 3 | def longest_common_substring(a, b, a_len = a.length, b_len = b.length, count = 0) 4 | return count if a_len <= 0 || b_len <= 0 5 | count = longest_common_substring(a, b, a_len - 1, b_len - 1, count + 1) if a[a_len - 1] == b[b_len - 1] 6 | [count, longest_common_substring(a, b, a_len - 1, b_len), longest_common_substring(a, b, a_len, b_len - 1)].max 7 | end 8 | 9 | p longest_common_substring("abdca", "cbda") 10 | p longest_common_substring("passport", "ppsspt") 11 | 12 | def longest_common_substring(a, b, a_len = a.length, b_len = b.length, current = "") 13 | return current if a_len <= 0 || b_len <= 0 14 | current = longest_common_substring(a, b, a_len - 1, b_len - 1, a[a_len - 1] + current) if a[a_len - 1] == b[b_len - 1] 15 | without_a = longest_common_substring(a, b, a_len - 1, b_len) 16 | without_b = longest_common_substring(a, b, a_len, b_len - 1) 17 | max_len = [current.length, without_a.length, without_b.length].max 18 | [current, without_a, without_b].find { |x| x.length == max_len } 19 | end 20 | 21 | p longest_common_substring("abdca", "cbda") 22 | p longest_common_substring("passport", "ppsspt") 23 | 24 | def longest_common_substring(a, b) 25 | dp = (a.length + 1).times.collect { (b.length + 1).times.collect { 0 } } 26 | max = 0 27 | max_start_idx = -1 28 | 1.upto(a.length) do |a_len| 29 | 1.upto(b.length) do |b_len| 30 | if a[a_len - 1] == b[b_len - 1] 31 | dp[a_len][b_len] = dp[a_len - 1][b_len - 1] + 1 32 | if dp[a_len][b_len] > max 33 | max = dp[a_len][b_len] 34 | max_start_idx = a_len - max 35 | end 36 | end 37 | end 38 | end 39 | max > 0 ? a.slice(max_start_idx, max) : "" 40 | end 41 | 42 | def longest_common_substring(a, b) 43 | a, b = b, a if a.length > b.length 44 | prev = (b.length + 1).times.collect { 0 } 45 | current = (b.length + 1).times.collect { 0 } 46 | max = 0 47 | max_start_idx = -1 48 | 1.upto(a.length) do |a_len| 49 | 1.upto(b.length) do |b_len| 50 | if a[a_len - 1] == b[b_len - 1] 51 | current[b_len] = prev[b_len - 1] + 1 52 | if current[b_len] > max 53 | max = current[b_len] 54 | max_start_idx = a_len - max 55 | end 56 | end 57 | end 58 | current, prev = prev, current 59 | end 60 | max > 0 ? a.slice(max_start_idx, max) : "" 61 | end 62 | 63 | p longest_common_substring("abdca", "cbda") 64 | p longest_common_substring("passport", "ppsspt") 65 | 66 | puts "longest_common_subsequence" 67 | 68 | def longest_common_subsequence(a, b, a_len = a.length, b_len = b.length) 69 | return 0 if a_len <= 0 || b_len <= 0 70 | if a[a_len - 1] == b[b_len - 1] 71 | 1 + longest_common_subsequence(a, b, a_len - 1, b_len - 1) 72 | else 73 | [longest_common_subsequence(a, b, a_len, b_len - 1), longest_common_subsequence(a, b, a_len - 1, b_len)].max 74 | end 75 | end 76 | 77 | p longest_common_subsequence("abdca", "cbda") 78 | p longest_common_subsequence("passport", "ppsspt") 79 | 80 | def longest_common_subsequence(a, b) 81 | a, b = b, a if a.length > b.length 82 | prev = [0] * (b.length + 1) 83 | cur = [0] * (b.length + 1) 84 | 1.upto(a.length) do |a_len| 85 | 1.upto(b.length) do |b_len| 86 | cur[b_len] = if a[a_len - 1] == b[b_len - 1] 87 | 1 + prev[b_len - 1] 88 | else 89 | [cur[b_len - 1], prev[b_len]].max 90 | end 91 | end 92 | prev, cur = cur, prev 93 | end 94 | prev[b.length] 95 | end 96 | 97 | def longest_common_subsequence(a, b) 98 | a, b = b, a if a.length > b.length 99 | dp = (a.length + 1).times.collect { [0] * (b.length + 1) } 100 | 1.upto(a.length) do |a_len| 101 | 1.upto(b.length) do |b_len| 102 | dp[a_len][b_len] = if a[a_len - 1] == b[b_len - 1] 103 | 1 + dp[a_len - 1][b_len - 1] 104 | else 105 | [dp[a_len][b_len - 1], dp[a_len - 1][b_len]].max 106 | end 107 | end 108 | end 109 | a_len, b_len = a.length, b.length 110 | res = "" 111 | while a_len > 0 && b_len > 0 112 | if a[a_len - 1] == b[b_len - 1] 113 | res = a[a_len - 1] + res 114 | a_len -= 1 115 | b_len -= 1 116 | elsif dp[a_len][b_len - 1] > dp[a_len - 1][b_len] 117 | b_len -= 1 118 | else 119 | a_len -= 1 120 | end 121 | end 122 | res 123 | end 124 | 125 | p longest_common_subsequence("abdca", "cbda") 126 | p longest_common_subsequence("passport", "ppsspt") 127 | 128 | puts "Min Deletions Insertions" 129 | 130 | def min_insertions_deletions_lcs(a, b, a_len = a.length, b_len = b.length) 131 | return 0 if a_len.zero? || b_len.zero? 132 | if a[a_len - 1] == b[b_len - 1] 133 | 1 + min_insertions_deletions_lcs(a, b, a_len - 1, b_len - 1) 134 | else 135 | [min_insertions_deletions_lcs(a, b, a_len - 1, b_len), min_insertions_deletions_lcs(a, b, a_len, b_len - 1)].max 136 | end 137 | end 138 | 139 | def min_insertions_deletions_lcs(a, b, a_len = a.length, b_len = b.length) 140 | a, b = b, a if a.length > b.length 141 | prev = [0] * (b.length + 1) 142 | cur = [0] * (b.length + 1) 143 | 1.upto(a.length) do |a_len| 144 | 1.upto(b.length) do |b_len| 145 | cur[b_len] = if a[a_len - 1] == b[b_len - 1] 146 | 1 + prev[b_len - 1] 147 | else 148 | [cur[b_len - 1], prev[b_len]].max 149 | end 150 | end 151 | prev, cur = cur, prev 152 | end 153 | prev[b.length] 154 | end 155 | 156 | def min_insertions_deletions(a, b) 157 | len = min_insertions_deletions_lcs(a, b) 158 | puts "Deletions : #{a.length - len}" 159 | puts "Additions : #{b.length - len}" 160 | end 161 | 162 | p min_insertions_deletions("abc", "fbc") 163 | p min_insertions_deletions("abdca", "cbda") 164 | p min_insertions_deletions("passport", "ppsspt") 165 | 166 | puts "longest increasing subsequence" 167 | 168 | def longest_increasing_subsequence(nums, len = nums.length, last = 1.0 / 0) 169 | return 0 if len.zero? 170 | if nums[len - 1] >= last 171 | longest_increasing_subsequence(nums, len - 1, last) 172 | else 173 | [1 + longest_increasing_subsequence(nums, len - 1, nums[len - 1]), longest_increasing_subsequence(nums, len - 1, last)].max 174 | end 175 | end 176 | 177 | p longest_increasing_subsequence [4, 2, 3, 6, 10, 1, 12] 178 | 179 | def longest_increasing_subsequence(nums) 180 | dp = [1] * nums.length 181 | max = 1 182 | 0.upto(nums.length - 1) do |idx| 183 | 0.upto(idx - 1) do |other_idx| 184 | if nums[other_idx] < nums[idx] && 1 + dp[other_idx] > dp[idx] 185 | dp[idx] = 1 + dp[other_idx] 186 | max = dp[idx] if dp[idx] > max 187 | end 188 | end 189 | end 190 | max 191 | end 192 | 193 | p longest_increasing_subsequence [4, 2, 3, 6, 10, 1, 12] 194 | 195 | puts "maximum_sum_increasing_subsequence" 196 | 197 | def maximum_sum_increasing_subsequence(nums, len = nums.length) 198 | return 0 if len.zero? 199 | max = nums[len - 1] 200 | val = nums[len - 1] 201 | 1.upto(len - 1) do |len| 202 | if nums[len - 1] < val 203 | current = maximum_sum_increasing_subsequence(nums, len) 204 | max = current + val if current + val >= max 205 | end 206 | end 207 | [max, maximum_sum_increasing_subsequence(nums, len - 1)].max 208 | end 209 | 210 | p maximum_sum_increasing_subsequence [4, 1, 2, 6, 10, 1, 12] 211 | p maximum_sum_increasing_subsequence [-4, 10, 3, 7, 15] 212 | p maximum_sum_increasing_subsequence [-4, 10, 3, 17, -4] 213 | 214 | def maximum_sum_increasing_subsequence(nums) 215 | dp = [0] * (nums.length) 216 | max = -1.0 / 0 217 | 0.upto(nums.length - 1) do |idx| 218 | dp[idx] = nums[idx] 219 | 0.upto(idx - 1) do |other_idx| 220 | if nums[other_idx] < nums[idx] && nums[idx] + dp[other_idx] > dp[idx] 221 | dp[idx] = nums[idx] + dp[other_idx] 222 | max = dp[idx] if dp[idx] > max 223 | end 224 | end 225 | end 226 | max 227 | end 228 | 229 | p maximum_sum_increasing_subsequence [4, 1, 2, 6, 10, 1, 12] 230 | p maximum_sum_increasing_subsequence [-4, 10, 3, 7, 15] 231 | p maximum_sum_increasing_subsequence [-4, 10, 3, 17, -4] 232 | 233 | puts "Shortest common supersequence" 234 | 235 | def shortest_common_supersequence(a, b, a_len = a.length, b_len = b.length) 236 | return [a_len, b_len].max if a_len.zero? || b_len.zero? 237 | if a[a_len - 1] == b[b_len - 1] 238 | 1 + shortest_common_supersequence(a, b, a_len - 1, b_len - 1) 239 | else 240 | 1 + [shortest_common_supersequence(a, b, a_len - 1, b_len), shortest_common_supersequence(a, b, a_len, b_len - 1)].min 241 | end 242 | end 243 | 244 | p shortest_common_supersequence "abcf", "bdcf" 245 | p shortest_common_supersequence("dynamic", "programming") 246 | p shortest_common_supersequence("dynamic", "programming") 247 | 248 | def shortest_common_supersequence(a, b) 249 | a, b = b, a if a.length > b.length 250 | prev = 0.upto(b.length).to_a 251 | cur = [0] * (b.length + 1) 252 | 1.upto(a.length) do |a_len| 253 | cur[0] = a_len 254 | 1.upto(b.length) do |b_len| 255 | cur[b_len] = if a[a_len - 1] == b[b_len - 1] 256 | 1 + prev[b_len - 1] 257 | else 258 | 1 + [cur[b_len - 1], prev[b_len]].min 259 | end 260 | end 261 | prev, cur = cur, prev 262 | end 263 | prev[b.length] 264 | end 265 | 266 | p shortest_common_supersequence "abcf", "bdcf" 267 | p shortest_common_supersequence("dynamic", "programming") 268 | p shortest_common_supersequence("dynamic", "programming") 269 | 270 | puts "Make deletions for sorted sequence" 271 | 272 | def min_deletions_for_sorted(nums, len = nums.length, prev = 1.0 / 0) 273 | return 0 if len.zero? 274 | if nums[len - 1] < prev 275 | [min_deletions_for_sorted(nums, len - 1, nums[len - 1]), 1 + min_deletions_for_sorted(nums, len - 1, prev)].min 276 | else 277 | 1 + min_deletions_for_sorted(nums, len - 1, prev) 278 | end 279 | end 280 | 281 | puts min_deletions_for_sorted [4, 2, 3, 6, 10, 1, 12] 282 | puts min_deletions_for_sorted [-4, 10, 3, 7, 15] 283 | puts min_deletions_for_sorted [3, 2, 1, 0] 284 | 285 | def min_deletions_for_sorted(nums, len = nums.length, prev = 1.0 / 0) 286 | dp = [1] * nums.length 287 | max = 1 288 | 0.upto(nums.length - 1) do |idx| 289 | 0.upto(idx - 1) do |other_idx| 290 | if nums[other_idx] < nums[idx] && dp[other_idx] >= dp[idx] 291 | dp[idx] = 1 + dp[other_idx] 292 | max = dp[idx] if dp[idx] > max 293 | end 294 | end 295 | end 296 | nums.length - max 297 | end 298 | 299 | puts min_deletions_for_sorted [4, 2, 3, 6, 10, 1, 12] 300 | puts min_deletions_for_sorted [-4, 10, 3, 7, 15] 301 | puts min_deletions_for_sorted [3, 2, 1, 0] 302 | 303 | puts "Longest repeating subsequence" 304 | 305 | def longest_repeating_subsequence(s, a_len = s.length, b_len = s.length - 1) 306 | return 0 if a_len.zero? || b_len.zero? 307 | if s[a_len - 1] == s[b_len - 1] && a_len != b_len 308 | 1 + longest_repeating_subsequence(s, a_len - 1, b_len - 1) 309 | else 310 | [longest_repeating_subsequence(s, a_len - 1, b_len), longest_repeating_subsequence(s, a_len, b_len - 1)].max 311 | end 312 | end 313 | 314 | p longest_repeating_subsequence "tomorrow" 315 | p longest_repeating_subsequence "aabdbcec" 316 | p longest_repeating_subsequence "fmff" 317 | 318 | def longest_repeating_subsequence(s) 319 | dp = (s.length + 1).times.collect { [0] * (s.length + 1) } 320 | 1.upto(s.length) do |a_len| 321 | 1.upto(s.length) do |b_len| 322 | dp[a_len][b_len] = if s[a_len - 1] == s[b_len - 1] && a_len != b_len 323 | 1 + dp[a_len - 1][b_len - 1] 324 | else 325 | [dp[a_len - 1][b_len], dp[a_len][b_len - 1]].max 326 | end 327 | end 328 | end 329 | a_len, b_len = s.length, s.length - 1 330 | res = "" 331 | while a_len > 0 && b_len > 0 332 | if s[a_len - 1] == s[b_len - 1] && a_len != b_len 333 | res = s[a_len - 1] + res 334 | a_len -= 1 335 | b_len -= 1 336 | elsif dp[a_len - 1][b_len] >= dp[a_len][b_len - 1] 337 | a_len -= 1 338 | else 339 | b_len -= 1 340 | end 341 | end 342 | res 343 | end 344 | 345 | p longest_repeating_subsequence "tomorrow" 346 | p longest_repeating_subsequence "aabdbcec" 347 | p longest_repeating_subsequence "fmff" 348 | 349 | puts "Count pattern" 350 | 351 | def pattern_count(s, p, s_len = s.length, p_len = p.length) 352 | return 0 if s_len.zero? 353 | if s[s_len - 1] == p[p_len - 1] 354 | if p_len == 1 355 | 1 + pattern_count(s, p, s_len - 1, p.length) 356 | else 357 | pattern_count(s, p, s_len - 1, p_len - 1) + pattern_count(s, p, s_len - 1, p_len) 358 | end 359 | else 360 | pattern_count(s, p, s_len - 1, p_len) 361 | end 362 | end 363 | 364 | p pattern_count "baxmx", "ax" 365 | p pattern_count "tomorrow", "tor" 366 | 367 | def pattern_count(s, p) 368 | prev = (p.length + 1).times.collect { 0 } 369 | cur = (p.length + 1).times.collect { 0 } 370 | prev[0], cur[0] = 1, 1 371 | 1.upto(s.length) do |s_len| 372 | 1.upto(p.length) do |p_len| 373 | cur[p_len] = if p[p_len - 1] == s[s_len - 1] 374 | prev[p_len - 1] + prev[p_len] 375 | else 376 | prev[p_len] 377 | end 378 | end 379 | cur, prev = prev, cur 380 | end 381 | prev[p.length] 382 | end 383 | 384 | p pattern_count "baxmx", "ax" 385 | p pattern_count "tomorrow", "tor" 386 | 387 | puts "Largest bitonic subsequence" 388 | 389 | def longest_increasing_subsequence(nums) 390 | dp = [1] * nums.length 391 | 0.upto(nums.length - 1) do |idx| 392 | (idx - 1).downto(0) do |other_idx| 393 | if nums[other_idx] < nums[idx] && dp[other_idx] >= dp[idx] 394 | dp[idx] = 1 + dp[other_idx] 395 | end 396 | end 397 | end 398 | dp 399 | end 400 | 401 | def longest_bitonic_subsequence(nums) 402 | dp = longest_increasing_subsequence(nums) 403 | dp_rev = longest_increasing_subsequence(nums.reverse) 404 | len = 0 405 | 1.upto(nums.length - 2) do |midx| 406 | sum = dp[midx] + dp_rev[midx] - 1 407 | len = sum if sum > len 408 | end 409 | len 410 | end 411 | 412 | p longest_bitonic_subsequence [4, 2, 3, 6, 10, 1, 12] 413 | p longest_bitonic_subsequence [4, 2, 5, 9, 7, 6, 10, 3, 1] 414 | 415 | puts "Longest alternatig subsequence" 416 | 417 | def longest_alternating_subsequence(nums, len = nums.length, decreasing = false, prev = decreasing ? 1.0 / 0 : -1.0 / 0) 418 | return 0 if len.zero? 419 | if decreasing && nums[len - 1] < prev 420 | [1 + longest_alternating_subsequence(nums, len - 1, false, nums[len - 1]), 421 | longest_alternating_subsequence(nums, len - 1, decreasing, prev)].max 422 | elsif !decreasing && nums[len - 1] > prev 423 | [1 + longest_alternating_subsequence(nums, len - 1, true, nums[len - 1]), 424 | longest_alternating_subsequence(nums, len - 1, decreasing, prev)].max 425 | else 426 | longest_alternating_subsequence(nums, len - 1, decreasing, prev) 427 | end 428 | end 429 | 430 | p longest_alternating_subsequence [1, 2, 3, 4] 431 | p longest_alternating_subsequence [3, 2, 1, 4] 432 | 433 | def longest_alternating_subsequence(nums) 434 | dp = nums.length.times.collect { [1, 1] } 435 | 0.upto(nums.length - 1) do |idx| 436 | (idx - 1).downto(0) do |other_idx| 437 | if nums[other_idx] < nums[idx] && dp[other_idx][1] >= dp[idx][0] 438 | dp[idx][0] = 1 + dp[other_idx][1] 439 | elsif nums[other_idx] > nums[idx] && dp[other_idx][0] >= dp[idx][1] 440 | dp[idx][1] = 1 + dp[other_idx][0] 441 | end 442 | end 443 | end 444 | dp[nums.length - 1].max 445 | end 446 | 447 | p longest_alternating_subsequence [1, 2, 3, 4] 448 | p longest_alternating_subsequence [3, 2, 1, 4] 449 | 450 | puts "Min edit distance" 451 | 452 | def min_edit_distance(s1, s2, s1_len = s1.length, s2_len = s2.length) 453 | return [s1_len, s2_len].max if s1_len.zero? || s2_len.zero? 454 | if s1[s1_len - 1] == s2[s2_len - 1] 455 | min_edit_distance(s1, s2, s1_len - 1, s2_len - 1) 456 | else 457 | [1 + min_edit_distance(s1, s2, s1_len - 1, s2_len - 1), 458 | 1 + min_edit_distance(s1, s2, s1_len, s2_len - 1), 459 | 1 + min_edit_distance(s1, s2, s1_len - 1, s2_len)].min 460 | end 461 | end 462 | 463 | p min_edit_distance "but", "bat" 464 | 465 | p min_edit_distance "passpot", "ppsspqrt" 466 | 467 | def min_edit_distance(s1, s2) 468 | prev = 0.upto(s2.length).to_a 469 | cur = [1.0 / 0] * (s2.length + 1) 470 | 1.upto(s1.length) do |s1_len| 471 | cur[0] = s1_len 472 | 1.upto(s2.length) do |s2_len| 473 | cur[s2_len] = if s1[s1_len - 1] == s2[s2_len - 1] 474 | prev[s2_len - 1] 475 | else 476 | 1 + [prev[s2_len - 1], 477 | cur[s2_len - 1], 478 | prev[s2_len]].min 479 | end 480 | end 481 | prev, cur = cur, prev 482 | end 483 | prev[s2.length] 484 | end 485 | 486 | p min_edit_distance "but", "bat" 487 | 488 | p min_edit_distance "passpot", "ppsspqrt" 489 | p min_edit_distance "passpot", "" 490 | 491 | p min_edit_distance "", "ppsspqrt" 492 | 493 | puts "string interleaving" 494 | 495 | def is_interleaving(a, b, p, a_len = a.length, b_len = b.length, p_len = p.length) 496 | return false if p_len != a_len + b_len 497 | return true if p_len.zero? 498 | if a_len > 0 && a[a_len - 1] == p[p_len - 1] 499 | is_interleaving(a, b, p, a_len - 1, b_len, p_len - 1) 500 | elsif b_len > 0 || b[b_len - 1] == p[p_len - 1] 501 | is_interleaving(a, b, p, a_len, b_len - 1, p_len - 1) 502 | else 503 | false 504 | end 505 | end 506 | 507 | p is_interleaving m = "abd", n = "cef", p = "abcdef" 508 | p is_interleaving m = "abd", n = "cef", p = "adcbef" 509 | p is_interleaving m = "", n = "abd", p = "abd" 510 | 511 | def is_interleaving(a, b, p) 512 | return false if p.length != a.length + b.length 513 | dp = (a.length + 1).times.collect { [false] * (b.length + 1) } 514 | dp[0][0] = true 515 | 1.upto(a.length) do |a_len| 516 | break unless a[a_len - 1] == p[a_len - 1] 517 | dp[a_len][0] = true 518 | end 519 | 1.upto(b.length) do |b_len| 520 | break unless b[b_len - 1] == p[b_len - 1] 521 | dp[0][b_len] = true 522 | end 523 | 1.upto(a.length) do |a_len| 524 | 1.upto(b.length) do |b_len| 525 | dp[a_len][b_len] = (a[a_len - 1] == p[a_len + b_len - 1] && dp[a_len - 1][b_len]) || 526 | (b[b_len - 1] == p[a_len + b_len - 1] && dp[a_len][b_len - 1]) 527 | end 528 | end 529 | dp[a.length][b.length] 530 | end 531 | 532 | def is_interleaving(a, b, p) 533 | return false if p.length != a.length + b.length 534 | prev = [false] * (b.length + 1) 535 | cur = [false] * (b.length + 1) 536 | prev[0] = true 537 | 1.upto(b.length) do |b_len| 538 | break unless b[b_len - 1] == p[b_len - 1] 539 | prev[b_len] = true 540 | end 541 | 1.upto(a.length) do |a_len| 542 | cur[0] = prev[0] && a[a_len - 1] == p[a_len - 1] 543 | 1.upto(b.length) do |b_len| 544 | cur[b_len] = (a[a_len - 1] == p[a_len + b_len - 1] && prev[b_len]) || 545 | (b[b_len - 1] == p[a_len + b_len - 1] && cur[b_len - 1]) 546 | end 547 | cur, prev = prev, cur 548 | end 549 | prev[b.length] 550 | end 551 | 552 | p is_interleaving m = "abd", n = "cef", p = "abcdef" 553 | p is_interleaving m = "abd", n = "cef", p = "adcbef" 554 | p is_interleaving m = "", n = "abd", p = "abd" 555 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grokking-the-Grokking-Dynamic-Programming-Patterns-for-Coding-Interviews 2 | Code for the educative course - Grokking Dynamic Programming Patterns for Coding Interviews - https://www.educative.io/collection/5668639101419520/5649050225344512 3 | --------------------------------------------------------------------------------