├── LICENSE ├── README.md ├── _config.yml ├── images ├── 3cards.png ├── past_future.png ├── top-down_and_bottom-up.drawio └── top-down_and_bottom-up.png └── javascript ├── 1025_divisor_game.js ├── 1035_uncrossed_lines.js ├── 1140_stone_game2.js ├── 120_triangle.js ├── 139_word_break.js ├── 1406_stone_game3.js ├── 140_word_break2.js ├── 1458_max_product_two_subsequences.js ├── 1463_cherry_pickup2.js ├── 1473_paint_house3.js ├── 1510_stone_game4.js ├── 1647_min_dels_unique_counts.js ├── 198_house_robber.js ├── 213_house_robber2.js ├── 221_maximal_square.js ├── 279_perfect_squares.js ├── 300_longest_increasing_subsequence.js ├── 322_coin_change.js ├── 337_house_robber3.js ├── 416_can_partition_equal_subset_sum.js ├── 518_coin_change2.js ├── 5_longest_palindromic_substring.js ├── 62_unique_paths.js ├── 673_max_LIS.js ├── 72_edit_distance.js ├── 746_min_cost_climbing_stairs.js ├── 787_cheapest_flights_within_k_stops.js ├── 799_champagne_tower.js ├── 877_stone_game.js ├── 91_decode_ways.js ├── 96_unique_binary_search_trees.js └── 983_min_cost_tickets.js /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎨 The ART of Dynamic Programming 2 | ## An Intuitive Approach: From Apprentice to Master 3 | 4 | > Making the simple complicated is commonplace; making the complicated simple, awesomely simple, that's creativity. 5 | > 6 | [-Charles Mingus Jr.](https://en.wikipedia.org/wiki/Charles_Mingus) 7 | 8 | Let us explore the intuitions of dynamic programming and transform our thoughts from "what the hell?" to "oh yeah, duh!" via a 3-step heuristic process. In hindsight, we can "see" the **ART** of dynamic programming is as easy as 1, 2, 3. 👍 9 | 10 | ## Prerequisites 11 | 12 | > You rarely have time for everything you want in this life, so you need to make choices. And hopefully your choices can come from a deep sense of who you are. 13 | > 14 | [-Mr. Rogers](https://en.wikipedia.org/wiki/Fred_Rogers) 15 | 16 | A significant amount of time and mental resources are necessary to begin understanding dynamic programming. Thus, DP is best approached as a "marathon" (not a "sprint"). Two key building blocks towards a basic understanding of DP are [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)) and [mathematical induction](https://en.wikipedia.org/wiki/Mathematical_induction). 17 | 18 | **Note:** When I mention "recursive stack unwinding" below, what I mean is "frames popping off the recursive [call stack](https://en.wikipedia.org/wiki/Call_stack)." And I imagine a similarity between this action and stepping down the stairs one-by-one, followed by stepping back up the stairs one-by-one in reverse. Stepping down the stairs would be equivalent to the recursive function being invoked as a subroutine of itself. And stepping back up the stairs would be equivalent to the recursive function invocations exiting their subroutines as the base case(s) are reached. 19 | 20 | > Take the first step in faith. You don't have to see the whole staircase, just take the first step. 21 | > 22 | [-Martin Luther King Jr.](https://en.wikipedia.org/wiki/Martin_Luther_King_Jr.) 23 | 24 | A mental leap of faith is necessary to begin post-processing [recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)) and [mathematical induction](https://en.wikipedia.org/wiki/Mathematical_induction), ie. we can only understand the basic principles of recursion and recursive algorithms after we have assumed the inductive hypothesis of a recurrence relation is true. After taking this mental leap of faith, we can look back in hindsight with the mind's eye to discover what that actually means, ie. we can clearly "see" the recursive stack hit the base case(s) and begin to unwind, formulating an optimal solution built upon the optimal solutions to subproblems of the original problem itself. 25 | 26 | 27 | ## What is Dynamic Programming? 28 | 29 | > We will never know our full potential unless we push ourselves to find it. 30 | > 31 | [-Travis Rice](https://en.wikipedia.org/wiki/Travis_Rice) 32 | 33 | There are two key ingredients to problems which can be solved by a [Dynamic Programming](https://en.wikipedia.org/wiki/Dynamic_programming) algorithm: 34 | 1. [Optimal Substructure](https://en.wikipedia.org/wiki/Optimal_substructure) 35 | 2. [Overlapping Subproblems](https://en.wikipedia.org/wiki/Overlapping_subproblems) 36 | 37 | 38 | ## What is the ART of Dynamic Programming? 39 | 40 | > Don't only practice your art, but force your way into its secrets. For it and knowledge can raise men to the divine. 41 | > 42 | [-Ludwig van Beethoven](https://en.wikipedia.org/wiki/Ludwig_van_Beethoven) 43 | 44 | **ART** is an acronym used to intuitively create solutions to DP problems in 3 steps: 45 | 1. **A**ll 46 | 2. **R**emember 47 | 3. **T**urn 48 | 49 | These 3 steps are elaborated upon below, however, let us first take a minute to consider our end-goal, and how we can intuitively reach it. Our end-goal is to minimize or maximize an objective function within the given constraints of an arbitrary universe of discourse (ie. the problem statement). 50 | 51 | **Ask yourself this question:** Is it possible to know the minimum or maximum objective function outcome without considering all possibilities? For example, let's say we have 3 numbers, and we want to know what is the minimum or maximum of those 3 numbers. Is it possible to know the minimum or maximum value *without* considering all of the numbers? Please take a moment to consider this question before proceeding. 52 | 53 |
See Answer 54 |

55 | 56 | The answer is obviously "no." It is not possible to know which of the 3 numbers are minimal or maximal unless we consider all 3 values. Using 3 cards as an example, let's assume we only know the values for the first two cards. Since we have not seen the third card's value, we don't know if it's less-than, equal-to, or greater-than the first two cards, and thus we cannot determine the objective function outcome without considering all of the card values. 57 | 58 |

59 | 60 |
61 |

62 | 63 |

64 | 65 | ### Step 1: All 66 | 67 | **A**ll possibilities of a universe of discourse under consideration need to be checked before we can determine the objective function outcome. This realization allows us to begin creating a DP solution via a naive [brute-force algorithm](https://en.wikipedia.org/wiki/Brute-force_search) ie. an exhaustive search of all possibilities. Therefore, we begin by exploring all possibilites via top-down [depth-first-search](https://en.wikipedia.org/wiki/Depth-first_search). Since we know we need to check all possibilities, this gives us key insight towards the N-dimensions of the corresponding DP memo which is used to remember the optimal solutions to each overlapping subproblem. This intuition leads us to step 2, but before we move on to step 2, let us first take another moment to consider the next key question. 68 | 69 | **Ask yourself this question:** Is it possible to determine the objective function outcome without solving overlapping subproblems more than once? 70 | 71 |
See Answer 72 |

73 | 74 | The answer is obviously "yes." With the properly structured N-dimensional memo we can store the optimal solutions to overlapping subproblems as they are computed, and then lookup previous solutions upon demand. This is the entire purpose of the DP memo. Simply remember each previous subproblem's optimal solution to avoid re-calculating it. In fact, this is raison d'être of dynamic programming, remembering the past to formulate the future, ie. use previous optimal subproblem solutions to formulate current optimal subproblem solutions to formulate the overall optimal solution for the original problem itself. 75 | 76 | 77 | 78 |

79 | 80 |
81 |

82 | 83 |

84 | 85 | ### Step 2: Remember 86 | 87 | **R**emember each previous subproblem's optimal solution to avoid recomputing it. Combined with the previous "top-down brute-force" solution from step 1, we create the "top-down with memo" solution by simply using a memo to store and lookup solutions to previously solved subproblems. Thus a simple if-statement is added to the top of the recursive function to check if a solution to the subproblem is available. If the solution is available, then return it immediately. If the solution is *not* available, then compute and store the solution once, thus making the solution available for future lookups. 88 | 89 | The memo is shaped as an arbitrary N-dimensional data structure such that each N-th dimension corresponds to a specific variable of the universe of discourse. Thus, the size of the N-dimensional data structure directly corresponds to the product of the coalesced variables of all possibilities under consideration. And it follows that the keys which uniquely identify each subproblem solution's storage position within the DP memo are all valid combinations of those specific variables per the constraints of the problem statement. The base case(s) of the recurrence relation are added to the memo first. And as the recursive stack unwinds, the base case(s) are iteratively and optimally built upon. This iterative building upon previous subproblem's optimal solutions from the bottom-up leads us to step 3. 90 | 91 | ### Step 3: Turn 92 | 93 | **T**urn the "top-down with memo" solution upside-down to formulate an explicit bottom-up solution. This step can be challenging because the base case(s) must first be explicitly specified *before* being iteratively built upon. The previous top-down solutions allow for the base case(s) to be implied by the recursion towards the base case(s) and thus implicitly stored by the memo as each recursive stack "bottoms out" (ie. hits the base case(s) and begins to unwind). To prepare for this implicit to explicit transformation, it can be helpful to print the key and value each time a subproblem's optimal solution is stored in the memo from the "top-down with memo" solution to explicitly identify the base case(s) and to clearly understand how the recursive stack unwinds and thus dictates the iterative building upon the bottom-up recurrence relation. It can also be helpful to print the entire memo when the memo's dimensions can be easily visualized, ie. within 1- or 2-dimensions. 94 | 95 | ### Step 4: Optional Memory Optimization 96 | 97 | It may be possible to further reduce the bottom-up solution's memory consumption. For example, if we have a 2D matrix for the DP memo, but the current row is only dependent upon the previous row, then we can reduce memory from O(N2) to O(N) by replacing "`dp[i]`" with "`cur`" (ie. current row) and "`dp[i - 1]`" with "`pre`" (ie. previous row). Furthermore, if "`cur`" is only dependent upon itself, then we can also remove "`pre`". See [322. Coin Change](https://leetcode.com/problems/coin-change/discuss/677858/Javascript-and-C%2B%2B-solutions) and [518. Coin Change 2](https://leetcode.com/problems/coin-change-2/discuss/677893/Javascript-and-C%2B%2B-solutions) as examples of this supplemental memory optimization which reduces the memory by a constant factor of N, ie. we only need N memory instead of 2 * N memory. 98 | 99 | 100 | ## Summary: The ART of Dynamic Programming 101 | 102 | > Computer, install a recursive algorithm. 103 | > 104 | -Ensign Harry Kim, [Star Trek Voyager, Episode 102](https://en.wikipedia.org/wiki/Nothing_Human_(Star_Trek:_Voyager)) 105 | 106 | The **ART** of DP in 3 steps: 107 | 108 | 1. **A**ll possibilities are considered via top-down brute-force depth-first-search 109 | 2. **R**emember each subproblem's optimal solution via a DP memo 110 | 3. **T**urn the top-down solution upside-down to create the bottom-up solution 111 | 112 | 113 | ## Visualization 114 | 115 | > Seeing is believing. 116 | > 117 | [-Far Seer (Warcraft III)](https://wowpedia.fandom.com/wiki/Far_Seer_(Warcraft_III)) 118 | 119 | Below is an oversimplified example which demonstrates the top-down solution's path (highlighted in yellow and green) and the bottom-up solution's path (highlighted in green). 120 | 121 | We can "see" the downward staircase of recursive calls highlighted in yellow and the corresponding optimal solutions formulated in reverse as the recursive stack unwinds back up the staircase highlighted in green. 122 | 123 | 124 | > You have to let it all go, Neo - fear, doubt, and disbelief... **Free your mind.** 125 | > 126 | [-Morpheus (The Matrix, 1999)](https://en.wikipedia.org/wiki/The_Matrix) 127 | 128 | ![](images/top-down_and_bottom-up.png) 129 | 130 | Each `i`th mental leap of faith from `i = 0..N-1` is highlighted in yellow as the recursive function `go()` invokes itself as a subroutine until the base case `N = 4` is reached, ie. we recursively `go()` from `here` to `there` over and over again. As the recursive stack unwinds (highlighted in green), `i`th sub-problem solutions are optimally built upon themselves. And we arrive at the same answer at the `End`. 131 | 132 | > True poets lead no one unawares. It is nothing other than awarness that poets - that is, creators of all sorts - seek. They do not display their art so as to make it appear real; they display the real in a way that reveals it to be art. 133 | > 134 | [-James P. Carse](https://en.wikipedia.org/wiki/James_P._Carse) 135 | 136 | The bottom-up solution has two key advantages over the top-down solution: 137 | 1. No recursive stack overhead 138 | 2. Memory optimization 139 | 140 | It's worth noting that we can perform linear scans from the top-down solutions from `i=0..N-1` or in the reverse direction from `i=N-1..0`, and likewise we can do the same for the bottom-up solutions. However, since the bottom-up solutions require explicit base cases to be defined to be iteratively built upon, it often makes sense to follow the recursive stack unwinding starting at `N-1` because we can explicitly add base case(s) by appending them onto index `N`,`N+1`,`N+2`... to be iteratively built upon. See [712. Minimum ASCII Delete Sum for Two Strings](https://leetcode.com/problems/minimum-ascii-delete-sum-for-two-strings/discuss/1606340/The-ART-of-Dynamic-Programming) as an example of bottom-up solutions performing linear scans in the opposite and same direction as the recursive stack unwinding, ie. starting from the top-left with empty string base cases at index `0` thus offsetting the `dp` matrix by `1` compared to starting from the bottom-right with empty string base cases at index `M`,`N` thus *not* needing to offset the `dp` matrix, simplifying the code. 141 | 142 | ## Canonical Examples 143 | 144 | > It seemed unthinkable for me to leave the world forever before I had produced all that I felt called upon to produce. 145 | > 146 | [-Ludwig van Beethoven](https://en.wikipedia.org/wiki/Ludwig_van_Beethoven) 147 | 148 | ### Emoji Legend 🧭 149 | * 🛑 **Base Case(s)** 150 | * Where the recursive stack "bottoms out" and begins to unwind 151 | * 🎯 **Recurrence Relation Target** 152 | * Determine the overall objective function outcome by recursively solving subproblems optimally 153 | * 🤔 **Memo** 154 | * Store and retrieve optimal solutions to previously solved subproblems within the N-dimensional memo data structure 155 | * 👀 **Seen** 156 | * Track which unique keys of the N-dimensional memo data structure have been previously seen 157 | * ✅ **With** 158 | * "include this item" concept used for 0-1 knapsack algorithms where we find the optimal solution by either including xor discluding each `i`th item 159 | * 🚫 **Without** 160 | * "exclude this item" concept used for 0-1 knapsack algorithms where we find the optimal solution by either including xor discluding each `i`th item 161 | * ❌ **Exit Conditions** 162 | * We can exit early under non-optimal conditions (ie. depth-first-search pruning) or for invalid inputs (ie. out-of-bounds) 163 | 164 | #### N-Dimensional Top-Down + Bottom-Up: 165 | 166 | > It is our conviction, based on considerable experience teaching the subject, that the art of formulating and solving problems using dynamic programming can be learned only through active participation by the student. No amount of passive listening to lectures or of reading text material prepares the student to formulate and solve novel problems. The student must first discover, by experience, that proper formulation is not quite as trivial as on his own, he will acquire the feel for the subject that ultimately renders proper formulation easy and natural. For this reason, this book contains a large number of instructional problems, carefully chosen to allow the student to acquire the art that we seek to convey. The student must do these problems on his own. Solutions are given in the back of the book because the reader needs feedback on the correctness of his procedures in order to learn, but any student who reads the solution before seriously attempting the problem does so at his own peril. He will almost certainly regret this passivity when faced with an examination or when confronted with real-world problems. We have seen countless students who have clearly understood and can religiously repeat our lectures on the dynamic programming solution of specific problems fail utterly on midterm examinations asking that the same techniques and ideas be used on similar, but different, problems. Surprised, and humbled, by this experience, the same students often then begin to take seriously our admonition to do homework problems and do not just read the solution and think “of course that is how to do them,” and many have written perfect final exams. Why dynamic programming is more art than science we do not know. We suspect that part of the problem is that students do not expect to be called upon to use common sense in advanced mathematical courses and, furthermore, have very little practice or experience with common sense in this context. But common sense, not mathematical manipulation, is what it takes to be successful in a course in dynamic programming. 167 | > 168 | [-*The Art and Theory of Dynamic Programming* Dreyfus and Law (1977)](https://www.academia.edu/8817530/The_Art_and_Theory_of_Dynamic_Programming) 169 | 170 | * [5. Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/discuss/635659/The-ART-of-Dynamic-Programming) 171 | * [38. Count and Say](https://leetcode.com/problems/count-and-say/discuss/581589/The-ART-of-Dynamic-Programming) 172 | * [45. Jump Game II](https://leetcode.com/problems/jump-game-ii/discuss/1533299/The-ART-of-Dynamic-Programming) 173 | * [55. Jump Game](https://leetcode.com/problems/jump-game/discuss/143388/The-ART-of-Dynamic-Programming) 174 | * [62. Unique Paths](https://leetcode.com/problems/unique-paths/discuss/22965/The-ART-of-Dynamic-Programming) 175 | * [63. Unique Paths II](https://leetcode.com/problems/unique-paths-ii/discuss/153003/The-ART-of-Dynamic-Programming) 176 | * [64. Minimum Path Sum](https://leetcode.com/problems/minimum-path-sum/discuss/23460/The-ART-of-Dynamic-Programming) 177 | * [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/discuss/131943/The-ART-of-Dynamic-Programming) 178 | * [72. Edit Distance](https://leetcode.com/problems/edit-distance/discuss/479377/The-ART-of-Dynamic-Programming) 179 | * [91. Decode Ways](https://leetcode.com/problems/decode-ways/discuss/117143/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 180 | * [96. Unique Binary Search Trees](https://leetcode.com/problems/unique-binary-search-trees/discuss/703865/The-ART-of-Dynamic-Programming) 181 | * [97. Interleaving String](https://leetcode.com/problems/interleaving-string/discuss/1247180/The-ART-of-Dynamic-Programming) 182 | * [120. Triangle](https://leetcode.com/problems/triangle/discuss/38726/The-ART-of-Dynamic-Programming) 183 | * [139. Word Break](https://leetcode.com/problems/word-break/discuss/632205/The-ART-of-Dynamic-Programming) 184 | * [140. Word Break II](https://leetcode.com/problems/word-break-ii/discuss/765548/Javascript-Python3-C%2B%2B-top-down-%2B-bottom-up-(partial)) 185 | * [198. House Robber](https://leetcode.com/problems/house-robber/discuss/846461/Javascript-Python3-C%2B%2B-The-ART-of-Dynamic-Programming) 186 | * [213. House Robber II](https://leetcode.com/problems/house-robber-ii/discuss/894504/The-ART-of-Dynamic-Programming) 187 | * [221. Maximal Square](https://leetcode.com/problems/maximal-square/discuss/600365/The-ART-of-Dynamic-Programming) 188 | * [256. Paint House](https://leetcode.com/problems/paint-house/discuss/68202/The-ART-of-Dynamic-Programming) 189 | * [264. Ugly Number II](https://leetcode.com/problems/ugly-number-ii/discuss/719537/The-ART-of-Dynamic-Programming) 190 | * [265. Paint House II](https://leetcode.com/problems/paint-house-ii/discuss/1249637/The-ART-of-Dynamic-Programming) 191 | * [279. Perfect Squares](https://leetcode.com/problems/perfect-squares/discuss/708644/Javascript-and-C%2B%2B-solutions) 192 | * [300. Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/discuss/385203/The-ART-of-Dynamic-Programming) 193 | * [309. Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/discuss/1566910/The-ART-of-Dynamic-Programming) 194 | * [322. Coin Change](https://leetcode.com/problems/coin-change/discuss/677858/The-ART-of-Dynamic-Programming) 195 | * [337. House Robber III](https://leetcode.com/problems/house-robber-iii/discuss/946524/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 196 | * [354. Russian Doll Envelopes](https://leetcode.com/problems/russian-doll-envelopes/discuss/1134924/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 197 | * [368. Largest Divisible Subset](https://leetcode.com/problems/largest-divisible-subset/discuss/1611788/The-ART-of-Dynamic-Programming) 198 | * [376. Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/discuss/1584820/The-ART-of-Dynamic-Programming) 199 | * [377. Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/discuss/1588390/The-ART-of-Dynamic-Programming) 200 | * [416. Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/discuss/617275/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 201 | * [473. Matchsticks to Square](https://leetcode.com/problems/matchsticks-to-square/discuss/1274510/The-ART-of-Dynamic-Programming) 202 | * [474. Ones and Zeroes](https://leetcode.com/problems/ones-and-zeroes/discuss/2067328/The-ART-of-Dynamic-Programming) 203 | * [486. Predict the Winner](https://leetcode.com/problems/predict-the-winner/discuss/3829734/The-ART-of-Dynamic-Programming) 204 | * [494. Target Sum](https://leetcode.com/problems/target-sum/discuss/1628944/The-ART-of-Dynamic-Programming) 205 | * [509. Fibonacci Number](https://leetcode.com/problems/fibonacci-number/discuss/595781/The-ART-of-Dynamic-Programming) 206 | * [514. Freedom Trail](https://leetcode.com/problems/freedom-trail/discuss/1147789/The-ART-of-Dynamic-Programming) 207 | * [516. Longest Palindromic Subsequence](https://leetcode.com/problems/longest-palindromic-subsequence/discuss/1583151/The-ART-of-Dynamic-Programming) 208 | * [518. Coin Change 2](https://leetcode.com/problems/coin-change-2/discuss/677893/The-ART-of-Dynamic-Programming) 209 | * [549. Binary Tree Longest Consecutive Sequence II](https://leetcode.com/problems/binary-tree-longest-consecutive-sequence-ii/discuss/2473918/The-ART-of-Dynamic-Programming) 210 | * [568. Maximum Vacation Days](https://leetcode.com/problems/maximum-vacation-days/discuss/1671107/The-ART-of-Dynamic-Programming) 211 | * [576. Out of Boundary Paths](https://leetcode.com/problems/out-of-boundary-paths/discuss/1660106/The-ART-of-Dynamic-Programming) 212 | * [583. Delete Operation for Two Strings](https://leetcode.com/problems/delete-operation-for-two-strings/discuss/2151454/The-ART-of-Dynamic-Programming) 213 | * [647. Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/discuss/105742/The-ART-of-Dynamic-Programming) 214 | * [650. 2 Keys Keyboard](https://leetcode.com/problems/2-keys-keyboard/discuss/1625343/The-ART-of-Dynamic-Programming) 215 | * [673. Number of Longest Increasing Subsequence](https://leetcode.com/problems/number-of-longest-increasing-subsequence/discuss/916696/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 216 | * [712. Minimum ASCII Delete Sum for Two Strings](https://leetcode.com/problems/minimum-ascii-delete-sum-for-two-strings/discuss/1606340/The-ART-of-Dynamic-Programming) 217 | * [714. Best Time to Buy and Sell Stock with Transaction Fee](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/1586778/The-ART-of-Dynamic-Programming) 218 | * [718. Maximum Length of Repeated Subarray](https://leetcode.com/problems/maximum-length-of-repeated-subarray/discuss/1325833/The-ART-of-Dynamic-Programming) 219 | * [746. Min Cost Climbing Stairs](https://leetcode.com/problems/min-cost-climbing-stairs/discuss/110111/The-ART-of-dynamic-programming) 220 | * [787. Cheapest Flights Within K Stops](https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/690997/Javascript-and-C%2B%2B-solutions) 221 | * [779. K-th Symbol in Grammar](https://leetcode.com/problems/k-th-symbol-in-grammar/discuss/4208832/The-ART-of-Dynamic-Programming) 222 | * [799. Champagne Tower](https://leetcode.com/problems/champagne-tower/discuss/118694/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming) 223 | * [823. Binary Trees With Factors](https://leetcode.com/problems/binary-trees-with-factors/discuss/126261/The-ART-of-Dynamic-Programming) 224 | * [877. Stone Game](https://leetcode.com/problems/stone-game/discuss/706734/Javascript-and-C%2B%2B-solutions) 225 | * [879. Profitable Schemes](https://leetcode.com/problems/profitable-schemes/discuss/155458/The-ART-of-Dynamic-Programming) 226 | * [931. Minimum Falling Path Sum](https://leetcode.com/problems/minimum-falling-path-sum/discuss/186646/The-ART-of-Dynamic-Programming) 227 | * [983. Minimum Cost For Tickets](https://leetcode.com/problems/minimum-cost-for-tickets/discuss/811237/Javascript-Python3-C%2B%2B-Top-Down-%2B-Bottom-Up) 228 | * [1025. Divisor Game](https://leetcode.com/problems/divisor-game/discuss/292472/Javascript-Python3-C%2B%2B-Top-Down-%2B-Bottom-Up) 229 | * [1029. Two City Scheduling](https://leetcode.com/problems/two-city-scheduling/discuss/1883096/The-ART-of-Dynamic-Programming) 230 | * [1035. Uncrossed Lines](https://leetcode.com/problems/uncrossed-lines/discuss/652184/The-ART-of-Dynamic-Programming) 231 | * [1043. Partition Array for Maximum Sum](https://leetcode.com/problems/partition-array-for-maximum-sum/solutions/4699420/the-art-of-dynamic-programming/) 232 | * [1137. N-th Tribonacci Number](https://leetcode.com/problems/n-th-tribonacci-number/discuss/345219/The-ART-of-Dynamic-Programming) 233 | * [1140. Stone Game II](https://leetcode.com/problems/stone-game-ii/discuss/713502/Javascript-and-C%2B%2B-solutions) 234 | * [1143. Longest Common Subsequence](https://leetcode.com/problems/longest-common-subsequence/discuss/1327645/The-ART-of-Dynamic-Programming) 235 | * [1230. Toss Strange Coins](https://leetcode.com/problems/toss-strange-coins/discuss/1609878/The-ART-of-Dynamic-Programming) 236 | * [1235. Maximum Profit in Job Scheduling](https://leetcode.com/problems/maximum-profit-in-job-scheduling/discuss/1477025/The-ART-of-Dynamic-Programming) 237 | * [1335. Minimum Difficulty of a Job Schedule](https://leetcode.com/problems/minimum-difficulty-of-a-job-schedule/discuss/1597586/The-ART-of-Dynamic-Programming) 238 | * [1345. Jump Game IV](https://leetcode.com/problems/jump-game-iv/discuss/519535/The-ART-of-Dynamic-Programming) 239 | * [1402. Reducing Dishes](https://leetcode.com/problems/reducing-dishes/discuss/574182/The-ART-of-Dynamic-Programming) 240 | * [1406. Stone Game III](https://leetcode.com/problems/stone-game-iii/discuss/657825/Javascript-and-C%2B%2B-solutions) 241 | * [1458. Max Dot Product of Two Subsequences](https://leetcode.com/problems/max-dot-product-of-two-subsequences/discuss/653625/Javascript-and-C%2B%2B-solutions) 242 | * [1463. Cherry Pickup II](https://leetcode.com/problems/cherry-pickup-ii/discuss/660828/The-ART-of-Dynamic-Programming) 243 | * [1473. Paint House III](https://leetcode.com/problems/paint-house-iii/discuss/695337/Javascript-and-C%2B%2B-solutions) 244 | * [1510. Stone Game IV](https://leetcode.com/problems/stone-game-iv/discuss/737869/javascript-python3-c) 245 | * [1690. Stone Game VII](https://leetcode.com/problems/stone-game-vii/discuss/1265150/The-ART-of-Dynamic-Programming) 246 | * [1696. Jump Game VI](https://leetcode.com/problems/jump-game-vi/discuss/1261177/The-ART-of-Dynamic-Programming) 247 | * [1751. Maximum Number of Events That Can Be Attended II](https://leetcode.com/problems/maximum-number-of-events-that-can-be-attended-ii/discuss/3770226/The-ART-of-Dynamic-Programming) 248 | * [1770. Maximum Score from Performing Multiplication Operations](https://leetcode.com/problems/maximum-score-from-performing-multiplication-operations/discuss/2584276/The-ART-of-Dynamic-Programming) 249 | * [1871. Jump Game VII](https://leetcode.com/problems/jump-game-vii/discuss/1231012/The-ART-of-Dynamic-Programming) 250 | * [1879. Minimum XOR Sum of Two Arrays](https://leetcode.com/problems/minimum-xor-sum-of-two-arrays/discuss/1241903/The-ART-of-Dynamic-Programming) 251 | * [1884. Egg Drop With 2 Eggs and N Floors](https://leetcode.com/problems/egg-drop-with-2-eggs-and-n-floors/discuss/1249918/The-ART-of-Dynamic-Programming) 252 | * [2008. Maximum Earnings From Taxi](https://leetcode.com/problems/maximum-earnings-from-taxi/discuss/1475113/The-ART-of-Dynamic-Programming) 253 | * [2140. Solving Questions With Brainpower](https://leetcode.com/problems/solving-questions-with-brainpower/discuss/1702845/The-ART-of-Dynamic-Programming) 254 | * [2209. Minimum White Tiles After Covering With Carpets](https://leetcode.com/problems/minimum-white-tiles-after-covering-with-carpets/discuss/1871574/The-ART-of-Dynamic-Programming) 255 | * [2212. Maximum Points in an Archery Competition](https://leetcode.com/problems/maximum-points-in-an-archery-competition/discuss/1873713/The-ART-of-Dynamic-Programming) 256 | * [2218. Maximum Value of K Coins From Piles](https://leetcode.com/problems/maximum-value-of-k-coins-from-piles/discuss/1901776/The-ART-of-Dynamic-Programming) 257 | * [2244. Minimum Rounds to Complete All Tasks](https://leetcode.com/problems/minimum-rounds-to-complete-all-tasks/discuss/1964309/The-ART-of-Dynamic-Programming) 258 | * [2247. Maximum Cost of Trip With K Highways](https://leetcode.com/problems/maximum-cost-of-trip-with-k-highways/discuss/2084932/The-ART-of-Dynamic-Programming) 259 | * [2266. Count Number of Texts](https://leetcode.com/problems/count-number-of-texts/discuss/2080203/The-ART-of-Dynamic-Programming) 260 | * [2267. Check if There Is a Valid Parentheses String Path](https://leetcode.com/problems/check-if-there-is-a-valid-parentheses-string-path/discuss/2162698/The-ART-of-Dynamic-Programming) 261 | * [2291. Maximum Profit From Trading Stocks](https://leetcode.com/problems/maximum-profit-from-trading-stocks/discuss/2136366/The-ART-of-Dynamic-Programming) 262 | * [2304. Minimum Path Cost in a Grid](https://leetcode.com/problems/minimum-path-cost-in-a-grid/discuss/2148689/The-ART-of-Dynamic-Programming) 263 | * [2328. Number of Increasing Paths in a Grid](https://leetcode.com/problems/number-of-increasing-paths-in-a-grid/discuss/2238522/The-ART-of-Dynamic-Programming) 264 | * [2361. Minimum Costs Using the Train Line](https://leetcode.com/problems/minimum-costs-using-the-train-line/discuss/2375565/The-ART-of-Dynamic-Programming) 265 | * [2369. Check if There is a Valid Partition For The Array](https://leetcode.com/problems/check-if-there-is-a-valid-partition-for-the-array/discuss/2390450/The-ART-of-Dynamic-Programming) 266 | * [2400. Number of Ways to Reach a Position After Exactly k Steps](https://leetcode.com/problems/number-of-ways-to-reach-a-position-after-exactly-k-steps/discuss/2534831/a-few-solutions) 267 | * [2431. Maximize Total Tastiness of Purchased Fruits](https://leetcode.com/problems/maximize-total-tastiness-of-purchased-fruits/discuss/2699915/The-ART-of-Dynamic-Programming) 268 | * [2466. Count Ways To Build Good Strings](https://leetcode.com/problems/count-ways-to-build-good-strings/discuss/2837361/The-ART-of-Dynamic-Programming) 269 | * [2556. Disconnect Path in a Binary Matrix by at Most One Flip](https://leetcode.com/problems/disconnect-path-in-a-binary-matrix-by-at-most-one-flip/discuss/3141457/The-ART-of-Dynamic-Programming) 270 | * [2581. Count Number of Possible Root Nodes](https://leetcode.com/problems/count-number-of-possible-root-nodes/discuss/3270368/The-ART-of-Dynamic-Programming) 271 | * [2585. Number of Ways to Earn Points](https://leetcode.com/problems/number-of-ways-to-earn-points/discuss/3266399/The-ART-of-Dynamic-Programming) 272 | * [2646. Minimize the Total Price of the Trips](https://leetcode.com/problems/minimize-the-total-price-of-the-trips/discuss/3439456/The-ART-of-Dynamic-Programming) 273 | * [2684. Maximum Number of Moves in a Grid](https://leetcode.com/problems/maximum-number-of-moves-in-a-grid/discuss/3531114/The-ART-of-Dynamic-Programming) 274 | * [2707. Extra Characters in a String](https://leetcode.com/problems/extra-characters-in-a-string/discuss/3568715/The-ART-of-Dynamic-Programming) 275 | * [2745. Construct the Longest New String](https://leetcode.com/problems/construct-the-longest-new-string/discuss/3677498/The-ART-of-Dynamic-Programming) 276 | * [2746. Decremental String Concatenation](https://leetcode.com/problems/decremental-string-concatenation/discuss/3678733/The-ART-of-Dynamic-Programming) 277 | * [2767. Partition String Into Minimum Beautiful Substrings](https://leetcode.com/problems/partition-string-into-minimum-beautiful-substrings/discuss/3737063/The-ART-of-Dynamic-Programming) 278 | * [2770. Maximum Number of Jumps to Reach the Last Index](https://leetcode.com/problems/maximum-number-of-jumps-to-reach-the-last-index/discuss/3741319/The-ART-of-Dynamic-Programming) 279 | * [2826. Sorting Three Groups](https://leetcode.com/problems/sorting-three-groups/discuss/3963615/The-ART-of-Dynamic-Programming) 280 | * [2928. Distribute Candies Among Children I](https://leetcode.com/problems/distribute-candies-among-children-i/discuss/4277848/The-ART-of-Dynamic-Programming) 281 | * [2944. Minimum Number of Coins for Fruits](https://leetcode.com/problems/minimum-number-of-coins-for-fruits/discuss/4337004/The-ART-of-Dynamic-Programming) 282 | * [2957. Remove Adjacent Almost-Equal Characters](https://leetcode.com/problems/remove-adjacent-almost-equal-characters/discuss/4382544/The-ART-of-Dynamic-Programming) 283 | * [3040. Maximum Number of Operations With the Same Score II](https://leetcode.com/problems/maximum-number-of-operations-with-the-same-score-ii/solutions/4742383/the-art-of-dynamic-programming/) 284 | * [3122. Minimum Number of Operations to Satisfy Conditions](https://leetcode.com/problems/minimum-number-of-operations-to-satisfy-conditions/solutions/5108929/the-art-of-dynamic-programming/) 285 | 286 | 287 | #### Kadane's Algorithm: Best Ending Here 288 | 289 | We can perform a linear scan tracking the optimal solution ending at a specific index via a dynamic programming technique similar to [Kadane's Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem). 290 | 291 | * [53. Maximum Subarray](https://leetcode.com/problems/maximum-subarray/discuss/561900/The-ART-of-Dynamic-Programming) 292 | * [121. Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/discuss/1540591/The-ART-of-Dynamic-Programming) 293 | * [122. Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/discuss/1540602/The-ART-of-Dynamic-Programming) 294 | * [135. Candy](https://leetcode.com/problems/candy/discuss/2238413/The-ART-of-Dynamic-Programming) 295 | * [152. Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/discuss/1535477/The-ART-of-Dynamic-Programming) 296 | * [329. Longest Increasing Path in a Matrix](https://leetcode.com/problems/longest-increasing-path-in-a-matrix/discuss/1622231/The-ART-of-Dynamic-Programming) 297 | * [413. Arithmetic Slices](https://leetcode.com/problems/arithmetic-slices/discuss/1456210/The-ART-of-Dynamic-Programming) 298 | * [487. Max Consecutive Ones II](https://leetcode.com/problems/max-consecutive-ones-ii/discuss/1601682/The-ART-of-Dynamic-Programming) 299 | * [525. Contiguous Array](https://leetcode.com/problems/contiguous-array/discuss/577638/The-ART-of-Dynamic-Programming) 300 | * [624. Maximum Distance in Arrays](https://leetcode.com/problems/maximum-distance-in-arrays/discuss/104653/The-ART-of-Dynamic-Programming) 301 | * [740. Delete and Earn](https://leetcode.com/problems/delete-and-earn/discuss/1521207/The-ART-of-Dynamic-Programming) 302 | * [918. Maximum Sum Circular Subarray](https://leetcode.com/problems/maximum-sum-circular-subarray/discuss/1535173/The-ART-of-Dynamic-Programming) 303 | * [1014. Best Sightseeing Pair](https://leetcode.com/problems/best-sightseeing-pair/discuss/1540576/the-art-of-dynamic-programming) 304 | * [1218. Longest Arithmetic Subsequence of Given Difference](https://leetcode.com/problems/longest-arithmetic-subsequence-of-given-difference/discuss/399886/The-ART-of-Dynamic-Programming) 305 | * [1372. Longest ZigZag Path in a Binary Tree](https://leetcode.com/problems/longest-zigzag-path-in-a-binary-tree/discuss/538986/The-ART-of-Dynamic-Programming) 306 | * [1425. Constrained Subsequence Sum](https://leetcode.com/problems/constrained-subsequence-sum/discuss/4193700/The-ART-of-Dynamic-Programming) 307 | * [1567. Maximum Length of Subarray With Positive Product](https://leetcode.com/problems/maximum-length-of-subarray-with-positive-product/discuss/1536710/The-ART-of-Dynamic-Programming) 308 | * [1746. Maximum Subarray Sum After One Operation](https://leetcode.com/problems/maximum-subarray-sum-after-one-operation/discuss/1602830/The-ART-of-Dynamic-Programming) 309 | * [2054. Two Best Non-Overlapping Events](https://leetcode.com/problems/two-best-non-overlapping-events/discuss/1549443/the-art-of-dynamic-programming) 310 | * [2110. Number of Smooth Descent Periods of a Stock](https://leetcode.com/problems/number-of-smooth-descent-periods-of-a-stock/discuss/1634993/the-art-of-dynamic-programming) 311 | * [2262. Total Appeal of A String](https://leetcode.com/problems/total-appeal-of-a-string/discuss/2014150/The-ART-of-Dynamic-Programming) 312 | * [2327. Number of People Aware of a Secret](https://leetcode.com/problems/number-of-people-aware-of-a-secret/discuss/2250957/The-ART-of-Dynamic-Programming) 313 | * [2393. Count Strictly Increasing Subarrays](https://leetcode.com/problems/count-strictly-increasing-subarrays/discuss/2540660/The-ART-of-Dynamic-Programming) 314 | * [2414. Length of the Longest Alphabetical Continuous Substring](https://leetcode.com/problems/length-of-the-longest-alphabetical-continuous-substring/discuss/2618498/The-ART-of-Dynamic-Programming) 315 | * [2501. Longest Square Streak in an Array](https://leetcode.com/problems/longest-square-streak-in-an-array/discuss/2916014/The-ART-of-Dynamic-Programming) 316 | * [2542. Maximum Subsequence Score](https://leetcode.com/problems/maximum-subsequence-score/discuss/3562295/The-ART-of-Dynamic-Programming) 317 | * [2712. Minimum Cost to Make All Characters Equal](https://leetcode.com/problems/minimum-cost-to-make-all-characters-equal/discuss/3591086/The-ART-of-Dynamic-Programming) 318 | * [2830. Maximize the Profit as the Salesman](https://leetcode.com/problems/maximize-the-profit-as-the-salesman/discuss/3936896/The-ART-of-Dynamic-Programming) 319 | * [2901. Longest Unequal Adjacent Groups Subsequence II](https://leetcode.com/problems/longest-unequal-adjacent-groups-subsequence-ii/discuss/4183760/The-ART-of-Dynamic-Programming) 320 | * [2943. Maximize Area of Square Hole in Grid](https://leetcode.com/problems/maximize-area-of-square-hole-in-grid/discuss/4336947/a-few-solutions) 321 | 322 | 323 | #### Recurrence Relation to Reduce Asymptotic Bounds via Pre-calculations: 324 | 325 | > Remember, concentrate on the moment. Feel, don't think. Trust your instincts. 326 | > 327 | [-Qui-Gon Jinn](https://www.starwars.com/databank/qui-gon-jinn) 328 | 329 | * [42. Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/discuss/1374857/The-ART-of-Dynamic-Programming) 330 | * [304. Range Sum Query 2D - Immutable](https://leetcode.com/problems/range-sum-query-2d-immutable/discuss/508260/The-ART-of-Dynamic-Programming) 331 | * [307. Range Sum Query - Mutable](https://leetcode.com/problems/range-sum-query-mutable/discuss/665390/Javascript-and-C%2B%2B-solutions) 332 | * [361. Bomb Enemy](https://leetcode.com/problems/bomb-enemy/discuss/1625489/The-ART-of-Dynamic-Programming) 333 | * [560. Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/discuss/592292/The-ART-of-Dynamic-Programming) 334 | * [562. Longest Line of Consecutive One in Matrix](https://leetcode.com/problems/longest-line-of-consecutive-one-in-matrix/discuss/1619862/The-ART-of-Dynamic-Programming) 335 | * [646. Maximum Length of Pair Chain](https://leetcode.com/problems/maximum-length-of-pair-chain/discuss/1611190/The-ART-of-Dynamic-Programming) 336 | * [764. Largest Plus Sign](https://leetcode.com/problems/largest-plus-sign/discuss/113350/The-ART-of-Dynamic-Programming) 337 | * [838. Push Dominoes](https://leetcode.com/problems/push-dominoes/discuss/1353112/The-ART-of-Dynamic-Programming) 338 | * [849. Maximize Distance to Closest Person](https://leetcode.com/problems/maximize-distance-to-closest-person/discuss/137957/The-ART-of-Dynamic-Programming) 339 | * [923. 3Sum With Multiplicity](https://leetcode.com/problems/3sum-with-multiplicity/discuss/1920122/The-ART-of-Dynamic-Programming) 340 | * [926. Flip String to Monotone Increasing](https://leetcode.com/problems/flip-string-to-monotone-increasing/discuss/186590/The-ART-of-Dynamic-Programming) 341 | * [1139. Largest 1-Bordered Square](https://leetcode.com/problems/largest-1-bordered-square/discuss/681894/Javascript-and-C%2B%2B-solutions) 342 | * [1182. Shortest Distance to Target Color](https://leetcode.com/problems/shortest-distance-to-target-color/discuss/401165/The-ART-of-Dynamic-Programming) 343 | * [1277. Count Square Submatrices with All Ones](https://leetcode.com/problems/count-square-submatrices-with-all-ones/discuss/442151/The-ART-of-Dynamic-Programming) 344 | * [1314. Matrix Block Sum](https://leetcode.com/problems/matrix-block-sum/discuss/480496/The-ART-of-Dynamic-Programming) 345 | * [1339. Maximum Product of Splitted Binary Tree](https://leetcode.com/problems/maximum-product-of-splitted-binary-tree/discuss/541816/The-ART-of-Dynamic-Programming) 346 | * [1493. Longest Subarray of 1's After Deleting One Element](https://leetcode.com/problems/longest-subarray-of-1s-after-deleting-one-element/discuss/708212/The-ART-of-Dynamic-Programming) 347 | * [1546. Maximum Number of Non-Overlapping Subarrays With Sum Equals Target](https://leetcode.com/problems/maximum-number-of-non-overlapping-subarrays-with-sum-equals-target/discuss/3308776/The-ART-of-Dynamic-Programming) 348 | * [1895. Largest Magic Square](https://leetcode.com/problems/largest-magic-square/discuss/1267999/brute-force-best-magic) 349 | * [1991. Find the Middle Index in Array](https://leetcode.com/problems/find-the-middle-index-in-array/discuss/1444083/a-few-solutions) 350 | * [2049. Count Nodes With the Highest Score](https://leetcode.com/problems/count-nodes-with-the-highest-score/discuss/1537470/The-ART-of-Dynamic-Programming) 351 | * [2055. Plates Between Candles](https://leetcode.com/problems/plates-between-candles/discuss/1549692/The-ART-of-Dynamic-Programming) 352 | * [2062. Count Vowel Substrings of a String](https://leetcode.com/problems/count-vowel-substrings-of-a-string/discuss/1683185/The-ART-of-Dynamic-Programming) 353 | * [2090. K Radius Subarray Averages](https://leetcode.com/problems/k-radius-subarray-averages/discuss/3659231/The-ART-of-Dynamic-Programming) 354 | * [2100. Find Good Days to Rob the Bank](https://leetcode.com/problems/find-good-days-to-rob-the-bank/discuss/1623340/The-ART-of-Dynamic-Programming) 355 | * [2132. Stamping the Grid](https://leetcode.com/problems/stamping-the-grid/discuss/1690137/The-ART-of-Dynamic-Programming) 356 | * [2155. All Divisions With the Highest Score of a Binary Array](https://leetcode.com/problems/all-divisions-with-the-highest-score-of-a-binary-array/discuss/1731659/The-ART-of-Dynamic-Programming) 357 | * [2163. Minimum Difference in Sums After Removal of Elements](https://leetcode.com/problems/minimum-difference-in-sums-after-removal-of-elements/discuss/1755896/The-ART-of-Dynamic-Programming) 358 | * [2167. Minimum Time to Remove All Cars Containing Illegal Goods](https://leetcode.com/problems/minimum-time-to-remove-all-cars-containing-illegal-goods/discuss/1763255/The-ART-of-Dynamic-Programming) 359 | * [2219. Maximum Sum Score of Array](https://leetcode.com/problems/maximum-sum-score-of-array/discuss/1903842/The-ART-of-Dynamic-Programming) 360 | * [2222. Number of Ways to Select Buildings](https://leetcode.com/problems/number-of-ways-to-select-buildings/discuss/1911959/The-ART-of-Dynamic-Programming) 361 | * [2247. Maximum Cost of Trip With K Highways](https://leetcode.com/problems/maximum-cost-of-trip-with-k-highways/discuss/2084932/The-ART-of-Dynamic-Programming) 362 | * [2266. Count Number of Texts](https://leetcode.com/problems/count-number-of-texts/discuss/2080203/The-ART-of-Dynamic-Programming) 363 | * [2337. Move Pieces to Obtain a String](https://leetcode.com/problems/move-pieces-to-obtain-a-string/discuss/2269717/The-ART-of-Dynamic-Programming) 364 | * [2381. Shifting Letters II](https://leetcode.com/problems/shifting-letters-ii/discuss/2454230/The-ART-of-Dynamic-Programming) 365 | * [2420. Find All Good Indices](https://leetcode.com/problems/find-all-good-indices/discuss/2623110/The-ART-of-Dynamic-Programming) 366 | * [2483. Minimum Penalty for a Shop](https://leetcode.com/problems/minimum-penalty-for-a-shop/discuss/2860657/The-ART-of-Dynamic-Programming) 367 | * [2485. Find the Pivot Integer](https://leetcode.com/problems/find-the-pivot-integer/discuss/2890914/a-few-solutions) 368 | * [2574. Left and Right Sum Differences](https://leetcode.com/problems/left-and-right-sum-differences/discuss/3236335/a-few-solutions) 369 | * [2587. Rearrange Array to Maximize Prefix Score](https://leetcode.com/problems/rearrange-array-to-maximize-prefix-score/discuss/3288490/The-ART-of-Dynamic-Programming) 370 | * [2602. Minimum Operations to Make All Array Elements Equal](https://leetcode.com/problems/minimum-operations-to-make-all-array-elements-equal/discuss/3348375/The-ART-of-Dynamic-Programming) 371 | * [2609. Find the Longest Balanced Substring of a Binary String](https://leetcode.com/problems/find-the-longest-balanced-substring-of-a-binary-string/discuss/3375188/The-ART-of-Dynamic-Programming) 372 | * [2659. Make Array Empty](https://leetcode.com/problems/make-array-empty/discuss/3490150/The-ART-of-Dynamic-Programming) 373 | * [2680. Maximum OR](https://leetcode.com/problems/maximum-or/discuss/3528755/The-ART-of-Dynamic-Programming) 374 | * [2711. Difference of Number of Distinct Values on Diagonals](https://leetcode.com/problems/difference-of-number-of-distinct-values-on-diagonals/discuss/3576060/The-ART-of-Dynamic-Programming) 375 | * [2780. Minimum Index of a Valid Split](https://leetcode.com/problems/minimum-index-of-a-valid-split/discuss/3779138/The-ART-of-Dynamic-Programming) 376 | * [2874. Maximum Value of an Ordered Triplet II](https://leetcode.com/problems/maximum-value-of-an-ordered-triplet-ii/solutions/6609264/a-few-solutions-by-claytonjwong-ehw5/) 377 | * [2971. Find Polygon With the Largest Perimeter](https://leetcode.com/problems/find-polygon-with-the-largest-perimeter/solutions/4732191/the-art-of-dynamic-programming/) 378 | * [3070. Count Submatrices with Top-Left Element and Sum Less Than k](https://leetcode.com/problems/count-submatrices-with-top-left-element-and-sum-less-than-k/solutions/4839042/the-art-of-dynamic-programming/) 379 | * [3096. Minimum Levels to Gain More Points](https://leetcode.com/problems/minimum-levels-to-gain-more-points/solutions/5077784/the-art-of-dynamic-programming/) 380 | * [3128. Right Triangles](https://leetcode.com/problems/right-triangles/solutions/5080684/the-art-of-dynamic-programming/) 381 | * [3147. Taking Maximum Energy From the Mystic Dungeon](https://leetcode.com/problems/taking-maximum-energy-from-the-mystic-dungeon/solutions/5149354/the-art-of-dynamic-programming/) 382 | * [3148. Maximum Difference Score in a Grid](https://leetcode.com/problems/maximum-difference-score-in-a-grid/solutions/5149652/the-art-of-dynamic-programming/) 383 | 384 | 385 | #### Bottom-Up Sequentially Building Upon "K-th Buckets" of Previous Solutions: 386 | 387 | > Your eyes deceive you, don't trust them. 388 | > 389 | [-Obi-Wan Kenobi](https://www.starwars.com/databank/obi-wan-kenobi) 390 | 391 | * [118. Pascal's Triangle](https://leetcode.com/problems/pascals-triangle/discuss/1288770/The-ART-of-Dynamic-Programming) 392 | * [119. Pascal's Triangle II](https://leetcode.com/problems/pascals-triangle-ii/discuss/787820/The-ART-of-Dynamic-Programming) 393 | * [207. Course Schedule](https://leetcode.com/problems/course-schedule/discuss/126917/The-ART-of-Dynamic-Programming) 394 | * [338. Counting Bits](https://leetcode.com/problems/counting-bits/discuss/657068/The-ART-of-Dynamic-Programming) 395 | * [523. Continuous Subarray Sum](https://leetcode.com/problems/continuous-subarray-sum/discuss/2746208/The-ART-of-Dynamic-Programming) 396 | * [659. Split Array into Consecutive Subsequences](https://leetcode.com/problems/split-array-into-consecutive-subsequences/discuss/106498/The-ART-of-Dynamic-Programming) 397 | * [974. Subarray Sums Divisible by K](https://leetcode.com/problems/subarray-sums-divisible-by-k/discuss/3090080/The-ART-of-Dynamic-Programming) 398 | * [1027. Longest Arithmetic Subsequence](https://leetcode.com/problems/longest-arithmetic-subsequence/discuss/544568/The-ART-of-Dynamic-Programming) 399 | * [1048. Longest String Chain](https://leetcode.com/problems/longest-string-chain/discuss/1214546/The-ART-of-Dynamic-Programming) 400 | * [1220. Count Vowels Permutation](https://leetcode.com/problems/count-vowels-permutation/discuss/4220179/The-ART-of-Dynamic-Programming) 401 | * [1262. Greatest Sum Divisible by Three](https://leetcode.com/problems/greatest-sum-divisible-by-three/discuss/439097/Javascript-and-C%2B%2B-solutions) 402 | * [1759. Count Number of Homogenous Substrings](https://leetcode.com/problems/count-number-of-homogenous-substrings/discuss/1064522/The-ART-of-Dynamic-Programming) 403 | * [1858. Longest Word With All Prefixes](https://leetcode.com/problems/longest-word-with-all-prefixes/discuss/2175291/The-ART-of-Dynamic-Programming) 404 | * [2121. Intervals Between Identical Elements](https://leetcode.com/problems/intervals-between-identical-elements/discuss/1657201/The-ART-of-Dynamic-Programming) 405 | * [2364. Count Number of Bad Pairs](https://leetcode.com/problems/count-number-of-bad-pairs/discuss/2490816/The-ART-of-Dynamic-Programming) 406 | * [2370. Longest Ideal Subsequence](https://leetcode.com/problems/longest-ideal-subsequence/discuss/2398567/The-ART-of-Dynamic-Programming) 407 | * [2579. Count Total Number of Colored Cells](https://leetcode.com/problems/count-total-number-of-colored-cells/discuss/3256118/The-ART-of-Dynamic-Programming) 408 | * [2588. Count the Number of Beautiful Subarrays](https://leetcode.com/problems/count-the-number-of-beautiful-subarrays/discuss/3288566/The-ART-of-Dynamic-Programming) 409 | * [2615. Sum of Distances](https://leetcode.com/problems/sum-of-distances/discuss/3405548/The-ART-of-Dynamic-Programming) 410 | * [2713. Maximum Strictly Increasing Cells in a Matrix](https://leetcode.com/problems/maximum-strictly-increasing-cells-in-a-matrix/discuss/3630962/The-ART-of-Dynamic-Programming) 411 | * [2786. Visit Array Positions to Maximize Score](https://leetcode.com/problems/visit-array-positions-to-maximize-score/discuss/3801798/The-ART-of-Dynamic-Programming) 412 | * [2870. Minimum Number of Operations to Make Array Empty](https://leetcode.com/problems/minimum-number-of-operations-to-make-array-empty/discuss/4109940/The-ART-of-Dynamic-Programming) 413 | * [2959. Number of Possible Sets of Closing Branches](https://leetcode.com/problems/number-of-possible-sets-of-closing-branches/discuss/4382664/The-ART-of-Dynamic-Programming) 414 | 415 | 416 | ## Subproblems and Recurrence Relations 417 | 418 | > You have only begun to discover your power. 419 | > 420 | [-Darth Vader](https://en.wikipedia.org/wiki/Darth_Vader) 421 | 422 | The most difficult part of dynamic programming is acquiring an understanding of a problem's subproblems and recurrence relations. It's amazing how difficult it can be to formulate DP solutions de novo with foresight conjured upon demand compared to validating existing DP solutions in hindsight. To develop a DP solution from scratch requires the creation of historical state variables and a series of states from which we consider all possibilities we can optimally choose from via recurrence relations which optimally build current subproblem solutions upon previous optimal subproblem solutions, ie. it is easier said than done. 423 | 424 | 425 | ## Resources 426 | 427 | > If you strike me down, I shall become more powerful than you can possibly imagine. 428 | > 429 | [-Obi-Wan Kenobi](https://en.wikipedia.org/wiki/Obi-Wan_Kenobi) 430 | 431 | * [Algorithms Illuminated](https://github.com/claytonjwong/Algorithms-Illuminated) 432 | * [Competitive Programmer's Core Skills by Saint Petersburg State University](https://claytonjwong.github.io/competitive-programming/) 433 | * [Algorithms by Stanford University](https://claytonjwong.github.io/Algorithms-Stanford/) 434 | * [Algorithms and Data Structures by UC San Diego](https://claytonjwong.github.io/Algorithms-UCSanDiego/) 435 | * [Algorithms for DNA Sequencing](https://claytonjwong.github.io/Algorithms-DNA-Sequencing/) 436 | * [Master Theorem: Determine the asymptotic bound of recursive algorithms via standard recurrences](https://claytonjwong.github.io/Master-Theorem/) 437 | * [Towers of Hanoi](https://claytonjwong.github.io/Towers-Of-Hanoi/) 438 | 439 | ## Supplemental Resources 440 | 441 | * [Mathematics for Computer Science](https://github.com/claytonjwong/Algorithms-Stanford/raw/master/documentation/mcs.pdf) 442 | * [Algorithms: Dasgupta-Papadimitriou-Vazirani ( 2006 )](https://github.com/claytonjwong/Algorithms-Stanford/tree/master/documentation/Dasgupta-Papadimitriou-Vazirani.pdf) 443 | * [Algorithms and Data Structures: Mehlhorn-Sanders ( 2007 )](https://github.com/claytonjwong/Algorithms-Stanford/tree/master/documentation/Mehlhorn-Sanders-Toolbox.pdf) 444 | * [Introduction to Algorithms: Cormen-Leiserson-Rivest-Stein ( 2009 )](https://en.wikipedia.org/wiki/Introduction_to_Algorithms) 445 | * [Discrete Probability](https://en.wikibooks.org/wiki/High_School_Mathematics_Extensions/Discrete_Probability) 446 | * [Mathematical Proofs](https://en.wikibooks.org/wiki/High_School_Mathematics_Extensions/Mathematical_Proofs) 447 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-time-machine 2 | show_downloads: true 3 | -------------------------------------------------------------------------------- /images/3cards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claytonjwong/The-ART-of-Dynamic-Programming/60d89e91e4a187282e11eae9d485e87a8a47d296/images/3cards.png -------------------------------------------------------------------------------- /images/past_future.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claytonjwong/The-ART-of-Dynamic-Programming/60d89e91e4a187282e11eae9d485e87a8a47d296/images/past_future.png -------------------------------------------------------------------------------- /images/top-down_and_bottom-up.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbc+I2FP41zOw+sCNLvvEYkpBOZ5vpNN1pt28GBHjWWKxskrC/vpJvWLIcDPEtQB6CLduyfL5zPn06kj1At+vXB+psVn+QOfYGEMxfB+huAKGmQ5P98JJdXGJpMC5YUneenLQveHJ/4aQQJKVbd44D4cSQEC90N2LhjPg+noVCmUMpeRFPWxBPvOvGWeJCwdPM8Yql/7jzcJWUagDsD/yG3eUqubVtJAemzuzHkpKtn9zPJz6Oj6ydtJrk1GDlzMlLrgjdD9AtJSSMt9avt9jjZk0tFl83KTmaNZliP6xywe+Iopvnh4f/fj7QifZ18Q18/2too7iaZ8fbJrYYQNNjFY6DjePzZoe7xErmzy1v61jYWvLfsROwpwa38c+nx89pHaw1cTXxeYkhshphZDnMGwjY4ZeVG+KnjTPjR1+Yp7GyVbj22J7GNj1nir1xZvBb4hHKDkUmZ80NKfmRocfPX7ieJ51ESeiELvFZyXDEb7kgfviUtIdfM3eCVdQeLWvuM6Yhfi01upZByaIDkzUO6Y6dklxgavYXy4ivSmJDRygretk7mzFK/GSV87P0PCfx72V2gz3QbCPB+gjcTasO3Al1l67v8LttKJl6eM3hd9m/6EIAro6QOsLQBqIX6PBLRScwQUNOMFL4wHtwydt5ANGE/SFUj/k0WwwizbYU5oNIYT7UlPm0w+bD/vyGd09sb+Y5QeDORIuJ5mW2oLt/8zvf+Q570GT37jV/8G6X7MW3xfNCHydZl3WqDl3i8I1nQmoU8i6qMHFaRrHHoupZbIbK7skd/iQua2AGMtRFkCGAYhUB2dIZTq7Kd3VSRehQRbEdChVFbpA99umeoR9PrnshMZzFIXTDDbqcfoKGwQkVsqaA/LahfS4ycnqfaVqwJJ+0PAtP5TMPMTN3q6+ccEXPdTx3ydlzxlwMs9aOeXi7TFHdJAfW7nzuRVSLA/eXM43q49664VaPcDDGA+PuLX5I5F5y8V5K5T27PDTLuRh8SWP1VI/dKS8gi0WAG/EoWPCoFaaR2OJ9rAQZM2MoohX3iVIfqOgWq6Oq6gpENmuN9c02Sb+ol8MUCe2MkUBG75CAFfrfvsgX2XxM2XcuX2CRUz6+foFGlwJG1ySY9RMFjHGoooYFDDwhLdCggoGXp2DiYDwrCVNUxZfYc6qpv92e0ygXMfCMoTBg/6BQ5eF6KmIK5jN7IGLsMxQxqASHdkSMIQ26oH2iiDEPVdS0iFHlN4viApWIi/ZVREleu1IYny407KaFhtjQtmVHSj0K2XFRfZ2SrNtNnRQH7JnsQGcMhTnqHxSqDHVPZYdsPgSUsiOV2e3Yr6igz0B2lPQE7cgOSxonodSkx8oO+1BFDcsOZFaSHY+XLTtQSaLubGRHcWCX9nWPw3POd1Sj63Z7u+IgMRMej4N0sYp+xqDYZhVQVItPGgNFr9CFvkeCADCZgIjwMHVZkzHll7r+UkAyv0yoBjvLqtsYdZ8iMWrN5YOBKpMPFIl8isMt9aNTpN6PbEJ3Ha0aC4i3jZZhZbR4epr/hGDNu0mdEftWjwoAqGuIJ+VXTKBaUqapqBY25mwVBhbHCeMyvAaVhW8sHA9GSGf5thESYUzXTh89aYgOVNSw8DXOkdFlOWMY3Y8+jfIRRkeMPo1Xfs+iHxWFJ23lS/OpqoLHK/Mf4ZTyWNkw1cyvcMrmmL/CTFb/mN/skvktUyRswzqR+S1woKKmmb/CLNyHY3554t5EPdDy5VNavdLy71uwc3F8Lq8bM5Uvh7Sr5NObfSw+H3XJ5zqUBLhxIp8j80BFDfO52fDS2k74XF5Mblrd87kJPwafXzMzR7ma/CKTafeAz1VpwL7zeRwfXfE5NCQaHp3I55qc4pEraprPG57u74bPxRiztB7QuSoD1kM6B1c6Py7dInoa7AGbq1KAvWfzTl/OsUUOttCJZA4P1NMwl1sqbX70lxuC7XSo/GSDoPQu+5MN6XR4j77YkOb1GgIfXsHPotzsIfq1fKynFH1pAv6y8ddRD/Evyvi/yWZ4R178OkRaA0uXBFTeXmc4maSoxl8HQ3XlVvdvqqfrr1Xq7S3hUT+QxXECxbMtDbi6SSU8TYNvzZ40kvAeZiYuHCYLdmjhuOxRZCcIVs6Gb84oCYKpQw+H6h6+QlAdB58Gq4Z2HWQtBaumfLFP09tU6FaFrytl+GyptxtTZlu+QLuHGOXpV7NrisyR9EksQ1ehZisnsZtCLW1SDrUxCUOyHn7bnCnH5rAd1YWsJJ1L5rNa5Vy7wpzGR4zHujAzoIjZEKVBdiAYrcY+UmgXkxx7vSN3g09s3B0WEGV3cTdBWSDlX6gINvGHRRfuK4fvGByjVySOw7FwZSZlax+3asq13rYi9uzGYq+4NijHqj1BMst4HY2keGWdSMqDUOXiw3ahLC71uffnb6CVcuqcbNmQ8/4CUdREEK3uQRwVVc51/PGeMSbo3/hjVNQ7V4zfo4+kWaBeYAyvGNeJsaW3iTHb3X/9PZ4e2n9dH93/Dw== -------------------------------------------------------------------------------- /images/top-down_and_bottom-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claytonjwong/The-ART-of-Dynamic-Programming/60d89e91e4a187282e11eae9d485e87a8a47d296/images/top-down_and_bottom-up.png -------------------------------------------------------------------------------- /javascript/1025_divisor_game.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1025. Divisor Game 3 | * 4 | * Q: https://leetcode.com/problems/divisor-game/ 5 | * A: https://leetcode.com/problems/divisor-game/discuss/292472/C%2B%2B-solutions%3A-Top-Down-and-Bottom-Up 6 | */ 7 | 8 | // brute-force 9 | let divisorGame = N => { 10 | let go = i => { 11 | if (i == 1) // 🛑 base case 12 | return false; 13 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) 14 | if (!(i % j) && !go(i - j)) 15 | return true; // 🎯 I win if I play j and you lose 16 | return false; 17 | }; 18 | return go(N); 19 | }; 20 | 21 | // memo 22 | let divisorGame = N => { 23 | let m = Array(N + 1).fill(null); 24 | let go = i => { 25 | if (m[i] != null) // 🤔 memo 26 | return m[i]; 27 | if (i == 1) // 🛑 base case 28 | return m[i] = false; 29 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) 30 | if (!(i % j) && !go(i - j)) 31 | return m[i] = true; // 🎯 I win if I play j and you lose 32 | return m[i] = false; 33 | }; 34 | return go(N); 35 | }; 36 | 37 | // bottom-up 38 | let divisorGame = N => { 39 | let dp = Array(N + 1).fill(false); // 🤔 memo with implicit 🛑 base case: dp[1] = false 40 | for (let i = 2; i <= N; ++i) 41 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) 42 | if (!(i % j) && !dp[i - j]) 43 | dp[i] = true; // 🎯 I win if I play j and you lose 44 | return dp[N]; 45 | }; 46 | -------------------------------------------------------------------------------- /javascript/1035_uncrossed_lines.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1035. Uncrossed Lines 3 | * 4 | * Q: https://leetcode.com/problems/uncrossed-lines/ 5 | * A: https://leetcode.com/problems/uncrossed-lines/discuss/652184/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // Top-Down: TLE 9 | let maxUncrossedLines = (A, B) => { 10 | let M = A.length, 11 | N = B.length; 12 | let go = (i = 0, j = 0) => { 13 | if (i == M || j == N) 14 | return 0; 15 | return Math.max( 16 | go(i + 1, j + 1) + Number(A[i] == B[j]), // match 🎯 / mismatch 17 | go(i, j + 1), go(i + 1, j) // insertion / deletion 18 | ); 19 | }; 20 | return go(); 21 | }; 22 | 23 | // Top-Down with Memo: AC 24 | let maxUncrossedLines = (A, B) => { 25 | let M = A.length, 26 | N = B.length; 27 | let m = [...Array(M + 1)].map(row => Array(N + 1).fill(-1)); 28 | let go = (i = 0, j = 0) => { 29 | if (m[i][j] > -1) 30 | return m[i][j]; 31 | if (i == M || j == N) 32 | return m[i][j] = 0; 33 | return m[i][j] = Math.max( 34 | go(i + 1, j + 1) + Number(A[i] == B[j]), // match 🎯 / mismatch 35 | go(i, j + 1), go(i + 1, j) // insertion / deletion 36 | ); 37 | }; 38 | return go(); 39 | }; 40 | 41 | // Bottom-Up: AC 42 | let maxUncrossedLines = (A, B) => { 43 | let M = A.length, 44 | N = B.length; 45 | let dp = [...Array(M + 1)].map(row => Array(N + 1).fill(0)); 46 | for (let i = 1; i <= M; ++i) 47 | for (let j = 1; j <= N; ++j) 48 | dp[i][j] = Math.max( 49 | dp[i - 1][j - 1] + Number(A[i - 1] == B[j - 1]), // match 🎯 / mismatch 50 | dp[i][j - 1], dp[i - 1][j] // insertion / deletion 51 | ); 52 | return dp[M][N]; 53 | }; 54 | 55 | // Bottom-Up Mem Opt: AC 56 | let maxUncrossedLines = (A, B) => { 57 | let M = A.length, 58 | N = B.length; 59 | let pre = Array(N + 1).fill(0), 60 | cur = [...pre]; 61 | for (let i = 1; i <= M; ++i) { 62 | for (let j = 1; j <= N; ++j) 63 | cur[j] = Math.max( 64 | pre[j - 1] + Number(A[i - 1] == B[j - 1]), // match 🎯 / mismatch 65 | cur[j - 1], pre[j] // insertion / deletion 66 | ); 67 | [pre, cur] = [cur, pre]; // swap 68 | } 69 | return pre[N]; 70 | }; -------------------------------------------------------------------------------- /javascript/1140_stone_game2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1140. Stone Game II 3 | * 4 | * Q: https://leetcode.com/problems/stone-game-ii/ 5 | * A: https://leetcode.com/problems/stone-game-ii/discuss/713502/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // top-down brute-force 9 | let stoneGameII = A => { 10 | let N = A.length; 11 | let go = (i, k, total, max = 0, take = 0) => { 12 | for (let j = i, stones = 1; j < Math.min(i + 2 * k, N); ++j, ++stones) // take each j-th stone until 🛑 base case: j == N 13 | max = Math.max(max, total - go(j + 1, Math.max(k, stones), total - (take += A[j]))); // 🎯 max my score minus max your score 14 | return max; 15 | }; 16 | return go(0, 1, A.reduce((a, b) => a + b)); 17 | }; 18 | 19 | // top-down memo 20 | let stoneGameII = A => { 21 | let N = A.length; 22 | let m = [...Array(N + 1)].map(_ => Array(N + 1).fill(0)); 23 | let go = (i, k, total, max = 0, take = 0) => { 24 | if (m[i][k]) 25 | return m[i][k]; // 🤔 memo 26 | for (let j = i, stones = 1; j < Math.min(i + 2 * k, N); ++j, ++stones) // take each j-th stone until 🛑 base case: j == N 27 | max = Math.max(max, total - go(j + 1, Math.max(k, stones), total - (take += A[j]))); // 🎯 max my score minus max your score 28 | return m[i][k] = max; 29 | }; 30 | return go(0, 1, A.reduce((a, b) => a + b)); 31 | }; 32 | 33 | // bottom-up 34 | let stoneGameII = A => { 35 | let N = A.length; 36 | let dp = [...Array(N + 1)].map(_ => Array(N + 1).fill(0)); // dp[i][j] == best total possible ending at i using last j from N-1..0 inclusive stones 37 | let total = Array(N + 1).fill(0); 38 | for (let i = N - 1; 0 <= i; --i) 39 | dp[i][N] = total[i] = total[i + 1] + A[i]; // 🛑 base cases: suffix sums, ie. when j == N, then initial state of "no stones taken" 40 | for (let i = N - 1; 0 <= i; --i) 41 | for (let j = N - 1; 1 <= j; --j) 42 | for (let k = 1; k <= 2 * j && i + k <= N; ++k) 43 | dp[i][j] = Math.max(dp[i][j], total[i] - dp[i + k][Math.max(j, k)]); // 🎯 max my score minus max your score 44 | return dp[0][1]; 45 | }; 46 | -------------------------------------------------------------------------------- /javascript/120_triangle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 120. Triangle 3 | * 4 | * Q: https://leetcode.com/problems/triangle/ 5 | * A: https://leetcode.com/problems/triangle/discuss/38726/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // TopDown 9 | let minimumTotal = A => { 10 | let N = A.length; 11 | let go = (i = 0, j = 0) => { 12 | if (i == N) 13 | return 0; 14 | return A[i][j] + Math.min(go(i + 1, j), go(i + 1, j + 1)); 15 | }; 16 | return go(); 17 | }; 18 | 19 | // TopDownMemo 20 | let minimumTotal = (A, m = new Map()) => { 21 | let N = A.length; 22 | let go = (i = 0, j = 0) => { 23 | let key = `${i},${j}`; 24 | if (m.has(key)) 25 | return m.get(key); 26 | if (i == N) 27 | return m.set(key, 0).get(key); 28 | return m.set(key, A[i][j] + Math.min(go(i + 1, j), go(i + 1, j + 1))).get(key); 29 | }; 30 | return go(); 31 | }; 32 | 33 | // BottomUp 34 | let minimumTotal = A => { 35 | let N = A.length; 36 | for (let i = N - 2; 0 <= i; --i) 37 | for (let j = 0; j < A[i].length; ++j) 38 | A[i][j] += Math.min(A[i + 1][j], A[i + 1][j + 1]); 39 | return A[0][0]; 40 | }; 41 | -------------------------------------------------------------------------------- /javascript/139_word_break.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 139. Word Break 3 | * 4 | * Q: https://leetcode.com/problems/word-break/ 5 | * A: https://leetcode.com/problems/word-break/discuss/632205/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // Top-Down TLE without memo 9 | let wordBreak = (s, words) => { 10 | let N = s.length; 11 | let dict = new Set(words); 12 | let go = (i = 0) => { 13 | if (i == N) // can we reach the N-th index? 🎯 14 | return 1; 15 | for (let j = i + 1; j <= N; ++j) 16 | if (dict.has(s.substring(i, j)) && go(j)) 17 | return 1; 18 | return 0; 19 | }; 20 | return go(); 21 | }; 22 | 23 | // Top-Down AC with memo 24 | let wordBreak = (s, words) => { 25 | let N = s.length; 26 | let m = Array(N).fill(-1); // memo 27 | let dict = new Set(words); 28 | let go = (i = 0) => { 29 | if (m[i] > -1) 30 | return m[i]; 31 | if (i == N) // can we reach the N-th index? 🎯 32 | return m[i] = 1; 33 | for (let j = i + 1; j <= N; ++j) 34 | if (dict.has(s.substring(i, j)) && go(j)) 35 | return m[i] = 1; 36 | return m[i] = 0; 37 | }; 38 | return go(); 39 | }; 40 | 41 | // Bottom-Up AC 42 | let wordBreak = (s, words) => { 43 | let N = s.length; 44 | let dp = Array(N + 1).fill(0); 45 | dp[0] = 1; // we can reach the 0-th index with no words 46 | for (let i = 0; i < N; ++i) { 47 | if (!dp[i]) 48 | continue; // i is not reachable ❌ 49 | for (let word of words) { 50 | let j = i + word.length; 51 | if (j <= N && word == s.substring(i, j)) 52 | dp[j] = 1; 53 | } 54 | } 55 | return dp[N]; // can we reach the N-th index? 🎯 56 | }; 57 | 58 | // Bottom-Up AC (minor memory optimization) 59 | let wordBreak = (s, words, reach = new Set([0])) => { // we can reach the 0-th index with no words 60 | let N = s.length; 61 | let dict = new Set(words); 62 | for (let i = 0; i < N; ++i) { 63 | if (!reach.has(i)) 64 | continue; // i is not reachable ❌ 65 | for (let word of words) { 66 | let j = i + word.length; 67 | if (j <= N && word == s.substring(i, j)) 68 | reach.add(j); 69 | } 70 | } 71 | return reach.has(N); // can we reach the N-th index? 🎯 72 | }; -------------------------------------------------------------------------------- /javascript/1406_stone_game3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1406. Stone Game III 3 | * 4 | * Q: https://leetcode.com/problems/stone-game-iii/ 5 | * A: https://leetcode.com/problems/stone-game-iii/discuss/657825/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // Top Down 9 | let stoneGameIII = A => { 10 | let N = A.length; 11 | let go = (i = 0, score = 0, max = -Infinity) => { 12 | if (i == N) 13 | return 0; 14 | for (let k = 0; k < 3 && i + k < N; ++k) 15 | max = Math.max(max, (score += A[i + k]) - go(i + k + 1)); 16 | return max; 17 | }; 18 | let max = go(); 19 | return 0 < max ? "Alice" : max < 0 ? "Bob" : "Tie"; 20 | }; 21 | 22 | // Top Down with Memo 23 | let stoneGameIII = A => { 24 | let N = A.length; 25 | let m = Array(N).fill(-Infinity); 26 | let go = (i = 0) => { 27 | if (i == N) 28 | return m[i] = 0; 29 | for (let k = 0, score = 0; k < 3 && i + k < N; ++k) 30 | m[i] = Math.max(m[i], (score += A[i + k]) - go(i + k + 1)); 31 | return m[i]; 32 | }; 33 | let max = go(); 34 | return 0 < max ? "Alice" : max < 0 ? "Bob" : "Tie"; 35 | }; 36 | 37 | // Bottom Up 38 | let stoneGameIII = A => { 39 | let N = A.length; 40 | let dp = Array(N).fill(-Infinity); 41 | for (let i = N - 1; 0 <= i; --i) 42 | for (let k = 0, score = 0; k < 3 && i + k < N; ++k) 43 | dp[i] = Math.max(dp[i], (score += A[i + k]) - (i + k + 1 < N ? dp[i + k + 1] : 0)); 44 | return 0 < dp[0] ? "Alice" : dp[0] < 0 ? "Bob" : "Tie"; 45 | }; -------------------------------------------------------------------------------- /javascript/140_word_break2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 140. Word Break II 3 | * 4 | * Q: https://leetcode.com/problems/word-break-ii/ 5 | * A: https://leetcode.com/problems/word-break-ii/discuss/765548/Javascript-Python3-C%2B%2B-top-down-%2B-bottom-up-(partial) 6 | */ 7 | 8 | // naive DFS + BT results in TLE which cannot be memoized, 9 | // since there is no return value to coalesce/memoize 10 | let wordBreak = (S, A, words = new Set(), ans = []) => { 11 | let N = S.length; 12 | A.forEach(word => words.add(word)); 13 | let go = (i = 0, path = []) => { 14 | if (i == N) { 15 | ans.push(path.join(' ')); 16 | return; 17 | } 18 | for (let j = i + 1; j <= N; ++j) { // ⭐️ candidate substrings S[i..j), ie. from i inclusive to j non-inclusive 19 | let cand = S.substring(i, j); 20 | if (words.has(cand)) 21 | go(j, path.concat(cand)); // 🚀 DFS + BT 22 | } 23 | }; 24 | go(); 25 | return ans; 26 | }; 27 | 28 | // brute-force 29 | let wordBreak = (S, A, dict = new Set()) => { 30 | let N = S.length; 31 | A.forEach(word => dict.add(word)); 32 | let go = (i = 0, words = []) => { 33 | if (i == N) // 🛑 base case: "empty" word can be constructed when there are no remaining characters in S 34 | return [[]]; 35 | for (let j = i + 1; j <= N; ++j) { // ⭐️ candidate substrings S[i..j), ie. from i inclusive to j non-inclusive 36 | let cand = S.substring(i, j); 37 | if (dict.has(cand)) 38 | for (let tail of go(j)) 39 | words.push([cand].concat(tail)); // 🚀 DFS concat tails onto 🔍 found candidates, ie. build 🎯 words from 👈 right-to-left 40 | } 41 | return words; 42 | }; 43 | return go().map(a => a.join(' ')); 44 | }; 45 | 46 | // memo 47 | let wordBreak = (S, A, dict = new Set()) => { 48 | let N = S.length; 49 | let m = Array(N + 1).fill(null); 50 | A.forEach(word => dict.add(word)); 51 | let go = (i = 0, words = []) => { 52 | if (m[i] != null) // 🤔 memo 53 | return m[i]; 54 | if (i == N) // 🛑 base case: "empty" word can be constructed when there are no remaining characters in S 55 | return m[i] = [[]]; 56 | for (let j = i + 1; j <= N; ++j) { // ⭐️ candidate substrings S[i..j), ie. from i inclusive to j non-inclusive 57 | let cand = S.substring(i, j); 58 | if (dict.has(cand)) 59 | for (let tail of go(j)) 60 | words.push([cand].concat(tail)); // 🚀 DFS concat tails onto 🔍 found candidates, ie. build 🎯 words from 👈 right-to-left 61 | } 62 | return m[i] = words; 63 | }; 64 | return go().map(a => a.join(' ')); 65 | }; 66 | 67 | // bottom-up 68 | let wordBreak = (S, A, dict = new Set()) => { 69 | let N = S.length; 70 | let dp = [...Array(N + 1)].map(_ => []); // 🤔 memo 71 | dp[N] = [[]]; // 🛑 base case: "empty" word can be constructed when there are no remaining characters in S 72 | A.forEach(word => dict.add(word)); 73 | for (let i = N - 1; 0 <= i; --i) { // ⭐️ candidate substrings S[i..j), ie. from i inclusive to j non-inclusive 74 | for (let j = i + 1; j <= N; ++j) { 75 | let cand = S.substring(i, j); 76 | if (dict.has(cand)) 77 | for (tail of dp[j]) 78 | dp[i].push([cand].concat(tail)); // 🚀 concat each tail onto the current candidate, 👈 ie. build the answer from right to left 79 | } 80 | } 81 | return dp[0].map(words => words.join(' ')); 82 | }; 83 | -------------------------------------------------------------------------------- /javascript/1458_max_product_two_subsequences.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1458. Max Dot Product of Two Subsequences 3 | * 4 | * Q: https://leetcode.com/problems/max-dot-product-of-two-subsequences/ 5 | * A: https://leetcode.com/problems/max-dot-product-of-two-subsequences/discuss/653625/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // hack for base case (when ans == 0 due to an empty subsequence of A or B) 9 | let maxDotProduct = (A, B) => { 10 | let M = A.length, 11 | N = B.length; 12 | let m = [...Array(M + 1)].map(row => Array(N + 1).fill(-1)); 13 | let go = (i = 0, j = 0) => { 14 | if (m[i][j] > -1) 15 | return m[i][j]; 16 | if (i == M || j == N) 17 | return m[i][j] = 0; 18 | return m[i][j] = Math.max( 19 | go(i + 1, j + 1) + A[i] * B[j], 20 | go(i, j + 1), go(i + 1, j) 21 | ); 22 | }; 23 | let ans = go(); 24 | if (ans) 25 | return ans; 26 | let max = -Infinity; 27 | A = [...new Set(A)]; 28 | B = [...new Set(B)]; 29 | A.forEach(x => B.forEach(y => max = Math.max(max, x * y))); 30 | return max; 31 | }; 32 | 33 | // properly handle base case (ie. do not allow empty subsequence of A or B): TLE without Memo 34 | let maxDotProduct = (A, B) => { 35 | let M = A.length, 36 | N = B.length; 37 | let go = (i = 0, j = 0) => { 38 | if (i == M || j == N) 39 | return 0; 40 | let max = A[i] * B[j] + Math.max(0, go(i + 1, j + 1)); // case 1: max(0, ...) to add recursive max product only if beneficial 41 | if (i < M - 1) max = Math.max(max, go(i + 1, j)); // case 2: without A[i], i < M - 1 to ensure subsequence of A is non-empty 42 | if (j < N - 1) max = Math.max(max, go(i, j + 1)); // case 3: without B[j], j < N - 1 to ensure subsequence of B is non-empty 43 | return max; // 🎯 44 | }; 45 | return go(); 46 | }; 47 | 48 | // properly handle base case (ie. do not allow empty subsequence of A or B): AC with memo 49 | let maxDotProduct = (A, B) => { 50 | let M = A.length, 51 | N = B.length; 52 | let m = [...Array(M + 1)].map(row => Array(N + 1).fill(-Infinity)); 53 | let go = (i = 0, j = 0) => { 54 | if (m[i][j] > -Infinity) 55 | return m[i][j]; 56 | if (i == M || j == N) 57 | return m[i][j] = 0; 58 | m[i][j] = A[i] * B[j] + Math.max(0, go(i + 1, j + 1)); // case 1: max(0, ...) to add recursive max product only if beneficial 59 | if (i < M - 1) m[i][j] = Math.max(m[i][j], go(i + 1, j)); // case 2: without A[i], i < M - 1 to ensure subsequence of A is non-empty 60 | if (j < N - 1) m[i][j] = Math.max(m[i][j], go(i, j + 1)); // case 3: without B[j], j < N - 1 to ensure subsequence of B is non-empty 61 | return m[i][j]; // 🎯 62 | }; 63 | return go(); 64 | }; 65 | 66 | // bottom-up 67 | let maxDotProduct = (A, B) => { 68 | let M = A.length, 69 | N = B.length; 70 | let dp = [...Array(M + 1)].map(row => Array(N + 1).fill(-Infinity)); // -INF ensures subsequences of A and B are non-empty 71 | for (let i = 1; i <= M; ++i) 72 | for (let j = 1; j <= N; ++j) 73 | dp[i][j] = Math.max( 74 | A[i - 1] * B[j - 1] + Math.max(0, dp[i - 1][j - 1]), // case 1: max(0, ...) to add recursive max product only if beneficial 75 | dp[i - 1][j], // case 2: without A[i] 76 | dp[i][j - 1], // case 3: without B[j] 77 | ); 78 | return dp[M][N]; // 🎯 79 | }; 80 | 81 | // bottom-up with memory optimization 82 | let maxDotProduct = (A, B) => { 83 | let M = A.length, 84 | N = B.length; 85 | let pre = Array(N + 1).fill(-Infinity), // -INF ensures subsequences of A and B are non-empty 86 | cur = [...pre]; 87 | for (let i = 1; i <= M; ++i) { 88 | for (let j = 1; j <= N; ++j) 89 | cur[j] = Math.max( 90 | A[i - 1] * B[j - 1] + Math.max(0, pre[j - 1]), // case 1: max(0, ...) to add recursive max product only if beneficial 91 | pre[j], // case 2: without A[i] 92 | cur[j - 1], // case 3: without B[j] 93 | ); 94 | [pre, cur] = [cur, pre]; // swap 95 | } 96 | return pre[N]; // 🎯 97 | }; 98 | -------------------------------------------------------------------------------- /javascript/1463_cherry_pickup2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1463. Cherry Pickup II 3 | * 4 | * Q: https://leetcode.com/problems/cherry-pickup-ii/ 5 | * A: https://leetcode.com/problems/cherry-pickup-ii/discuss/660828/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // top-down 9 | let cherryPickup = A => { 10 | let M = A.length, 11 | N = A[0].length; 12 | let go = (k = 0, i = 0, j = N - 1) => { 13 | if (k == M) 14 | return 0; 15 | let best = 0; 16 | for (let u of [i - 1, i, i + 1]) 17 | for (let v of [j - 1, j, j + 1]) 18 | if (!(u < 0 || v < 0 || u == M || v == N || v <= u)) 19 | best = Math.max(best, go(k + 1, u, v)); 20 | return A[k][i] + A[k][j] + best; 21 | }; 22 | return go(); 23 | }; 24 | 25 | // top-down w/ memo 26 | let cherryPickup = (A, m = new Map()) => { 27 | let M = A.length, 28 | N = A[0].length; 29 | let go = (k = 0, i = 0, j = N - 1) => { 30 | let key = `${k},${i},${j}`; 31 | if (m.has(key)) 32 | return m.get(key); 33 | if (k == M) 34 | return m.set(key, 0).get(key); 35 | let best = 0; 36 | for (let u of [i - 1, i, i + 1]) 37 | for (let v of [j - 1, j, j + 1]) 38 | if (!(u < 0 || v < 0 || u == M || v == N || v <= u)) 39 | best = Math.max(best, go(k + 1, u, v)); 40 | return m.set(key, A[k][i] + A[k][j] + best).get(key); 41 | }; 42 | return go(); 43 | }; 44 | 45 | // bottom-up 46 | let cherryPickup = (A, m = new Map()) => { 47 | let M = A.length, 48 | N = A[0].length; 49 | let dp = [...Array(M + 1)].map(_ => [...Array(N)].map(_ => Array(N).fill(0))); 50 | for (let k = M - 1; 0 <= k; --k) 51 | for (let i = 0; i < N; ++i) 52 | for (let j = 0; j < N; ++j) 53 | for (let u of [i - 1, i, i + 1]) 54 | for (let v of [j - 1, j, j + 1]) 55 | if (!(u < 0 || v < 0 || u == M || v == N || v <= u)) 56 | dp[k][i][j] = Math.max(dp[k][i][j], A[k][i] + A[k][j] + dp[k + 1][u][v]); 57 | return dp[0][0][N - 1]; 58 | }; 59 | 60 | // bottom-up mem-opt 61 | let cherryPickup = (A, m = new Map()) => { 62 | let M = A.length, 63 | N = A[0].length; 64 | let pre = [...Array(N)].map(_ => Array(N).fill(0)); 65 | for (let k = M - 1; 0 <= k; --k) { 66 | let cur = [...Array(N)].map(_ => Array(N).fill(0)); 67 | for (let i = 0; i < N; ++i) 68 | for (let j = 0; j < N; ++j) 69 | for (let u of [i - 1, i, i + 1]) 70 | for (let v of [j - 1, j, j + 1]) 71 | if (!(u < 0 || v < 0 || u == M || v == N || v <= u)) 72 | cur[i][j] = Math.max(cur[i][j], A[k][i] + A[k][j] + pre[u][v]); 73 | [pre, cur] = [cur, pre]; 74 | } 75 | return pre[0][N - 1]; 76 | }; 77 | -------------------------------------------------------------------------------- /javascript/1473_paint_house3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1473. Paint House III 3 | * 4 | * Q: https://leetcode.com/problems/paint-house-iii/ 5 | * A: https://leetcode.com/problems/paint-house-iii/discuss/695337/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | /* 9 | 10 | ok so we're basically assigning values to the 0s from 1..N inclusive as candidates 11 | 12 | keep track of target t neighborhoods: 13 | if assigning same value as neighbors, then same target t neighborhoods, 14 | otherwise if assigning a difference value, then decrement the target t neighborhoods by 1 15 | 16 | houses to A 17 | rename m to N <-- length of A 18 | and n to K 19 | 20 | ok so here's the deal: let's start with top-down brute-force DFS, and add a memo to avoid TLE 21 | 22 | ^^ hopefully that's a good start, let's see 23 | 24 | for each i-th position of A, 25 | if set to 0, then we need to optimally choose a color for A[i] 26 | either 27 | choose the same color (if applicable as neighbors and use same target t neighborhoods) 28 | choose a difference color than neighbors and use target t - 1 neighborhoods 29 | 30 | base case occurs when: 31 | 32 | t == 0 33 | 34 | the cost accumulate thus far is valid if i == N? the length of A or if all values of A to the end of A are set 35 | so track R as the right-most 0 (this is the last house we need to pay the cost to paint) 36 | 37 | keep track of the current i-th house, as houses are set, increment i <-- maybe just easier to create a set of indexes needed to be painted... let's do that instead, ok so if i == paint.length, then we're golden when t == 0 38 | 39 | cost[i][j]: is the cost of paint the house i with the color j+1. 40 | 41 | sum will be accumulate back up as the recursive stack unwinds 42 | 43 | 44 | post-mortem analysis: 45 | 46 | * during the contest I attempted to perform a "pre-optimization" by iterating only over the indices with value 0, 47 | ie. houses which need to be painted, however, this does NOT correctly count the target t amount of neighborhoods correctly! 48 | 49 | * after the contest I still made this problem more difficult that it needed to be and still ended up with an incorrect solution 50 | 51 | * KEY INSIGHT: keep track of the previous neighbor and use it to determine if the neighborhood is the same or different!!! 52 | 53 | */ 54 | 55 | // 56 | // complicated and incorrect algorithm 1 57 | // 58 | 59 | /** 60 | * @param {number[]} houses 61 | * @param {number[][]} cost 62 | * @param {number} m 63 | * @param {number} n 64 | * @param {number} target 65 | * @return {number} 66 | */ 67 | // let minCost = (A, cost, N, K, T, paint = []) => { // A.length == N, 1..K inclusive colors, target T neighborhoods 68 | // A.forEach((x, i) => { if (!x) paint.push(i); }); 69 | // console.log(...paint) 70 | // let go = (i = 0, t = T, sum = 0, best = Infinity) => { 71 | // if (i == paint.length) 72 | // console.log(`i: ${i} t: ${t} sum: ${sum}`); 73 | // if (!t) 74 | // return i == paint.length ? sum : Infinity; 75 | // if (i == paint.length) 76 | // return Infinity; 77 | // for (let j = 0; j < K; ++j) { // j-th candidate color 78 | // A[paint[i]] = j + 1; // forward track 79 | // let L = A[paint[i]], 80 | // R = A[paint[i]]; 81 | // if (0 <= paint[i] - 1) 82 | // L = A[paint[i] - 1]; 83 | // if (paint[i] + 1 <= N - 1) 84 | // R = A[paint[i] + 1]; 85 | // // console.log(`forward: [${A}]`) 86 | // if (L == A[paint[i]] && (R == 0 || R == A[paint[i]])) { // same neighborbood 87 | // // console.log(`same neighborhood`) 88 | // best = Math.min(best, go(i + 1, t, sum + cost[paint[i]][j])); 89 | // } else { 90 | // // console.log(`diff neighborhood`) 91 | // best = Math.min(best, go(i + 1, t - 1, sum + cost[paint[i]][j])); 92 | // } 93 | // // console.log(`j-th best: ${j} ${best}`) 94 | // A[paint[i]] = 0; // backtrack 95 | // // console.log(`back: [${A}]`) 96 | // } 97 | // return best; 98 | // }; 99 | // return go(); 100 | // }; 101 | 102 | // 103 | // complicated and incorrect algorithm 2 104 | // 105 | 106 | // let minCost = (A, cost, N, K, T) => { // A.length == N, 1..K inclusive colors, target T neighborhoods 107 | // let m = [...Array(N + 1)].map(_ => [...Array(T + 1)].map(_ => Array(K + 1).fill(-1))); 108 | // let sameHood = i => { 109 | // let L = 0 <= i - 1 ? A[i - 1] : null, 110 | // R = i + 1 < N ? A[i + 1] : null; 111 | // if (L == null && R == null) 112 | // return false; // cannot be same as neighbor if there are no neighbors 113 | // if (L == null) 114 | // return A[i] == R; 115 | // if (R == null) 116 | // return L == A[i]; 117 | // return L == A[i] || A[i] == R; 118 | // }; 119 | // let go = (i = 0, t = T, min = Infinity) => { 120 | // if (t < 0) 121 | // return Infinity; 122 | // if (i == N) 123 | // return /*m[i][t][A[i]] =*/ !t ? 0 : Infinity; 124 | // if (A[i]) // i-th house is already painted, skip past it (I messed this up during the contest by pre-optimizing a solution to skip past homes already painted) 125 | // return m[i][t][A[i]] = go(i + 1, t - Number(t == T || !sameHood(i))); // t == T means the first hood is counted 126 | // for (let j = 0; j < K; ++j) { // 0-based j-th color from 0..K-1 inclusive 127 | // A[i] = j + 1; // +1 for 1-based indexing 128 | // min = Math.min(min, cost[i][j] + go(i + 1, t - Number(t == T || !sameHood(i)))); // t == T means the first hood is counted 129 | // A[i] = 0; 130 | // } 131 | // return m[i][t][A[i]] = min; 132 | // }; 133 | // return go() < Infinity ? go() : -1; 134 | // }; 135 | 136 | // DFS 137 | let minCost = (A, cost, N, K, T) => { 138 | let go = (i = 0, color = 0, t = T, min = Infinity) => { 139 | let diffHood = cand => Number(cand != color); // different neighborhood 🏘 🏘 140 | if (t < 0) 141 | return Infinity; // invalid target t neighborhoods ❌ 142 | if (i == N) 143 | return !t ? 0 : Infinity; // target 🎯 t == 0 144 | if (A[i]) 145 | return go(i + 1, A[i], t - diffHood(A[i])); // A[i] is already painted, thus A[i] is the only j-th color under consideration 146 | for (let j = 0; j < K; ++j) 147 | min = Math.min(min, cost[i][j] + go(i + 1, j + 1, t - diffHood(j + 1))); // j + 1 for 1-based indexing 148 | return min; 149 | }; 150 | let min = go(); 151 | return min < Infinity ? min : -1; 152 | }; 153 | 154 | // DFS with Memo 155 | let minCost = (A, cost, N, K, T) => { 156 | let m = [...Array(N + 1)].map(_ => [...Array(K + 1)].map(_ => Array(T + 1).fill(-1))); 157 | let go = (i = 0, color = 0, t = T, min = Infinity) => { 158 | let diffHood = cand => Number(cand != color); // different neighborhood 🏘 🏘 159 | if (t < 0) 160 | return Infinity; // invalid target t neighborhoods ❌ 161 | if (m[i][color][t] > -1) 162 | return m[i][color][t]; // memo 🤔 163 | if (i == N) 164 | return m[i][color][t] = !t ? 0 : Infinity; // target 🎯 t == 0 165 | if (A[i]) 166 | return m[i][color][t] = go(i + 1, A[i], t - diffHood(A[i])); // A[i] is already painted, thus A[i] is the only j-th color under consideration 167 | for (let j = 0; j < K; ++j) 168 | min = Math.min(min, cost[i][j] + go(i + 1, j + 1, t - diffHood(j + 1))); // j + 1 for 1-based indexing 169 | return m[i][color][t] = min; 170 | }; 171 | let min = go(); 172 | return min < Infinity ? min : -1; 173 | }; 174 | 175 | console.log(` 9 == ${minCost([0,0,0,0,0], [[1,10],[10,1],[10,1],[1,10],[5,1]], 5, 2, 3)}`); 176 | console.log(`11 == ${minCost([0,2,1,2,0], [[1,10],[10,1],[10,1],[1,10],[5,1]], 5, 2, 3)}`); 177 | console.log(` 5 == ${minCost([0,0,0,0,0], [[1,10],[10,1],[1,10],[10,1],[1,10]], 5, 2, 5)}`); 178 | console.log(`-1 == ${minCost([3,1,2,3], [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], 4, 3, 3)}`); 179 | console.log(` 0 == ${minCost([2,2,1], [[1,1],[3,4],[4,2]], 3, 2, 2)}`); 180 | console.log(`12 == ${minCost([0,0,0,1], [[1,5],[4,1],[1,3],[4,4]], 4, 2, 4)}`); 181 | console.log(`24 == ${minCost([0,1,0,0,1,2,0,0,2,1], [[4,5,2,6],[8,3,2,9],[6,7,3,1],[10,10,2,7],[6,5,2,4],[4,4,3,9],[9,8,3,5],[7,9,10,3],[8,5,9,10],[10,7,4,6]], 10, 4, 6)}`); 182 | -------------------------------------------------------------------------------- /javascript/1510_stone_game4.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1510. Stone Game IV 3 | * 4 | * Q: https://leetcode.com/problems/stone-game-iv/ 5 | * A: https://leetcode.com/problems/stone-game-iv/discuss/737869/Javascript-Python3-C%2B%2B 6 | */ 7 | 8 | // top-down 9 | let winnerSquareGame = N => { 10 | let go = i => { 11 | if (!i) 12 | return false; // 🛑 base case: no stones left 13 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) // ⭐️ take each (j * j) amount of stones 14 | if (!go(i - (j * j))) 15 | return true; // 🎯 if next player lost, then current player wins 16 | return false; 17 | }; 18 | return go(N); 19 | }; 20 | 21 | // top-down memo 22 | let winnerSquareGame = N => { 23 | let m = Array(N).fill(null); 24 | let go = i => { 25 | if (m[i] != null) 26 | return m[i]; // 🤔 memo 27 | if (!i) 28 | return m[i] = false; // 🛑 base case: no stones left 29 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) // ⭐️ take each (j * j) amount of stones, 💎 avoid TLE by taking largest (j * j) first 30 | if (!go(i - (j * j))) 31 | return m[i] = true; // 🎯 if next player lost, then current player wins 32 | return m[i] = false; 33 | }; 34 | return go(N); 35 | }; 36 | 37 | // bottom-up 38 | let winnerSquareGame = N => { 39 | let dp = Array(N + 1).fill(false); // 🤔 memo 40 | dp[0] = false; // 🛑 base case: no stones left 41 | for (let i = 1; i <= N; ++i) 42 | for (let j = Math.floor(Math.sqrt(i)); 1 <= j; --j) // ⭐️ take each (j * j) amount of stones, 💎 avoid TLE by taking largest (j * j) first 43 | if (!dp[i - (j * j)]) 44 | dp[i] = true; // 🎯 if next player lost, then current player wins 45 | return dp[N]; 46 | }; 47 | -------------------------------------------------------------------------------- /javascript/1647_min_dels_unique_counts.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 1647. Minimum Deletions to Make Character Frequencies Unique 3 | * 4 | * Q: https://leetcode.com/problems/minimum-deletions-to-make-character-frequencies-unique/ 5 | * A: https://leetcode.com/problems/minimum-deletions-to-make-character-frequencies-unique/discuss/927497/Kt-Js-Py3-Cpp-Map-%2B-Seen-Counts 6 | */ 7 | 8 | let minDeletions = (s, m = new Map(), seen = new Set(), dels = 0) => { 9 | s.split('').forEach(c => m.set(c, 1 + (m.get(c) || 0))); 10 | for (let [_, cnt] of [...m.entries()]) { 11 | while (cnt && seen.has(cnt)) 12 | ++dels, 13 | --cnt; 14 | seen.add(cnt); 15 | } 16 | return dels; 17 | }; 18 | -------------------------------------------------------------------------------- /javascript/198_house_robber.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 198. House Robber 3 | * 4 | * Q: https://leetcode.com/problems/house-robber/ 5 | * A: https://leetcode.com/problems/house-robber/discuss/846461/Javascript-Python3-C%2B%2B-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // brute-force 9 | let rob = A => { 10 | let go = (pre = -2, cur = 0) => { 11 | if (cur == A.length) 12 | return 0; // 🛑 base case 13 | else if (pre == cur - 1) 14 | return go(pre, cur + 1); // 🚫 without (due to adjacent neighbor constraint) 15 | else 16 | return Math.max(A[cur] + go(cur, cur + 1), go(pre, cur + 1)); // # ✅ with or 🚫 without 17 | }; 18 | return go(); 19 | }; 20 | 21 | // memo 22 | let rob = (A, m = new Map()) => { 23 | let go = (pre = -2, cur = 0) => { 24 | let key = `${pre},${cur}`; 25 | if (m.has(key)) 26 | return m.get(key); // 🤔 memo 27 | if (cur == A.length) 28 | m.set(key, 0); // 🛑 base case 29 | else if (pre == cur - 1) 30 | m.set(key, go(pre, cur + 1)); // 🚫 without (due to adjacent neighbor constraint) 31 | else 32 | m.set(key, Math.max(A[cur] + go(cur, cur + 1), go(pre, cur + 1))); // # ✅ with or 🚫 without 33 | return m.get(key); 34 | }; 35 | return go(); 36 | }; 37 | 38 | // bottom-up 39 | let rob = A => { 40 | let N = A.length; 41 | let dp = Array(N + 2).fill(0); // 🤔 memo +2 for 🛑 base cases 42 | for (let i = N - 1; 0 <= i; --i) 43 | dp[i] = Math.max(A[i] + dp[i + 2], dp[i + 1]); // # ✅ with or 🚫 without 44 | return Math.max(dp[0], dp[1]); 45 | }; 46 | -------------------------------------------------------------------------------- /javascript/213_house_robber2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 213. House Robber II 3 | * 4 | * Q: https://leetcode.com/problems/house-robber-ii/ 5 | * A: https://leetcode.com/problems/house-robber-ii/discuss/894504/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // brute-force 9 | let rob = A => { 10 | let N = A.length; 11 | let go = (i, first, last) => { 12 | if (i == N) // 🛑 base case 13 | return 0; 14 | if (last + 1 == i || (first && i + 1 == N && 1 < N)) // 🚫 without i-th house (due to adjacent neighbor constraint) 15 | return go(i + 1, first, last); 16 | return Math.max(A[i] + go(i + 1, first, i), go(i + 1, first, last)); // ✅ with i-th house xor 🚫 without i-th house 17 | }; 18 | return Math.max(go(0, true, -123), go(1, false, -123)); // ✅ with first house xor 🚫 without first house 19 | }; 20 | 21 | // top-down memo 22 | let rob = (A, m = {}) => { 23 | let N = A.length; 24 | let go = (i, first, last) => { 25 | let key = `${i},${first},${last}`; 26 | if (m[key] != undefined) // 🤔 memo 27 | return m[key]; 28 | if (i == N) // 🛑 base case 29 | return m[key] = 0; 30 | if (last + 1 == i || (first && i + 1 == N && 1 < N)) // 🚫 without i-th house (due to adjacent neighbor constraint) 31 | return m[key] = go(i + 1, first, last); 32 | return m[key] = Math.max(A[i] + go(i + 1, first, i), go(i + 1, first, last)); // ✅ with i-th house xor 🚫 without i-th house 33 | }; 34 | return Math.max(go(0, true, -123), go(1, false, -123)); // ✅ with first house xor 🚫 without first house 35 | }; 36 | 37 | // bottom-up 38 | let rob = A => { 39 | let N = A.length; 40 | if (N == 1) // 💎 corner case 41 | return A[0]; 42 | let best = start => { 43 | let dp = Array(N + 2).fill(0); // 🤔 memo + 🛑 base cases (ie. dp[N] = 0 and dp[N + 1] = 0) 44 | for (let i = N - 1 - (start ? 0 : 1); start <= i; --i) 45 | dp[i] = Math.max(A[i] + dp[i + 2], dp[i + 1]) // ✅ with i-th house xor 🚫 without i-th house 46 | return dp[start]; 47 | }; 48 | return Math.max(best(0), best(1)); // ✅ with first house xor 🚫 without first house 49 | }; 50 | 51 | // bottom-up memory optimization 52 | let rob = A => { 53 | let N = A.length; 54 | if (N == 1) // 💎 corner case 55 | return A[0]; 56 | let best = start => { 57 | let [ a, b, c ] = [ 0, 0, 0 ]; // 🤔 memo + 🛑 base cases (ie. a = 0 and b = 0) 58 | for (let i = N - 1 - (start ? 0 : 1); start <= i; --i) 59 | c = Math.max(A[i] + a, b), // ✅ with i-th house xor 🚫 without i-th house 60 | a = b, b = c; // 👉 slide window 61 | return c; 62 | }; 63 | return Math.max(best(0), best(1)); // ✅ with first house xor 🚫 without first house 64 | }; 65 | -------------------------------------------------------------------------------- /javascript/221_maximal_square.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 221. Maximal Square 3 | * 4 | * Q: https://leetcode.com/problems/maximal-square/ 5 | * A: https://leetcode.com/problems/maximal-square/discuss/600365/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // top-down 9 | let maximalSquare = (A, max = 0) => { 10 | let M = A.length, 11 | N = M ? A[0].length : 0; 12 | let m = [...Array(M + 1)].map(row => Array(N + 1).fill(-1)); 13 | let go = (i, j) => { 14 | if (m[i][j] > -1) 15 | return m[i][j]; 16 | if (!i || !j) 17 | return m[i][j] = 0; 18 | let best = 0; 19 | if (A[i - 1][j - 1] == '1') 20 | best = 1 + Math.min(go(i - 1, j - 1), go(i - 1, j), go(i, j - 1)); 21 | max = Math.max(max, best * best); 22 | return m[i][j] = best; 23 | }; 24 | for (let i = 1; i <= M; ++i) 25 | for (let j = 1; j <= N; ++j) 26 | go(i, j); 27 | return max; 28 | }; 29 | 30 | // bottom-up 31 | let maximalSquare = (A, max = 0) => { 32 | let M = A.length, 33 | N = M ? A[0].length : 0; 34 | let dp = [...Array(M + 1)].map(row => Array(N + 1).fill(0)); 35 | for (let i = 1; i <= M; ++i) 36 | for (let j = 1; j <= N; ++j) 37 | if (A[i - 1][j - 1] == '1') 38 | dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]), 39 | max = Math.max(max, dp[i][j] * dp[i][j]); 40 | return max; 41 | } 42 | 43 | // bottom-up (memory optimized) 44 | let maximalSquare = (A, max = 0) => { 45 | let M = A.length, 46 | N = M ? A[0].length : 0; 47 | let pre = Array(N + 1).fill(0), 48 | cur = Array(N + 1).fill(0); 49 | for (let i = 1; i <= M; ++i, [[pre, cur]] = [[cur, pre]]) 50 | for (let j = 1; j <= N; ++j) 51 | if (A[i - 1][j - 1] == '1') 52 | cur[j] = 1 + Math.min(pre[j - 1], pre[j], cur[j - 1]), 53 | max = Math.max(max, cur[j] * cur[j]); 54 | else 55 | cur[j] = 0; 56 | return max; 57 | } -------------------------------------------------------------------------------- /javascript/279_perfect_squares.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 279. Perfect Squares 3 | * 4 | * Q: https://leetcode.com/problems/perfect-squares/ 5 | * A: https://leetcode.com/problems/perfect-squares/discuss/708644/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // top-down brute-force 9 | let numSquares = N => { 10 | let go = (i, min = Infinity) => { 11 | if (i <= 3) 12 | return i; // base cases 🛑 13 | for (let x = Math.floor(Math.sqrt(i)); 0 < x; --x) 14 | if (0 <= i - x * x) 15 | min = Math.min(min, 1 + go(i - x * x)); // min squares x*x to reach sum i 🎯 16 | return min; 17 | } 18 | return go(N); 19 | }; 20 | 21 | // top-down with memo 22 | let numSquares = (N, m = new Map()) => { 23 | let go = (i = N, min = Infinity) => { 24 | if (m.has(i)) 25 | return m.get(i); // memo 🤔 26 | if (i <= 3) 27 | return m.set(i, i).get(i); // base cases 🛑 28 | for (let x = Math.floor(Math.sqrt(i)); 0 < x; --x) 29 | if (0 <= i - x * x) 30 | min = Math.min(min, 1 + go(i - x * x)); // min squares x*x to reach sum i 🎯 31 | return m.set(i, min).get(i); 32 | } 33 | return go(); 34 | }; 35 | 36 | // bottom-up 37 | let numSquares = N => { 38 | let dp = Array(N + 1).fill(Infinity); 39 | for (let i = 0; i <= N && i <= 3; dp[i] = i, ++i); // base cases 🛑 40 | for (let i = 4; i <= N; ++i) { 41 | for (let x = Math.floor(Math.sqrt(i)); 0 < x; --x) 42 | if (0 <= i - x * x) 43 | dp[i] = Math.min(dp[i], 1 + dp[i - x * x]); // min squares x*x to reach sum i 🎯 44 | } 45 | return dp[N]; 46 | }; 47 | 48 | // bottom-up optimized 49 | let numSquares = N => { 50 | let dp = [...Array(N + 1).keys()]; // implicit base cases 🛑 51 | for (let i = 4; i <= N; ++i) { 52 | for (let x = Math.floor(Math.sqrt(i)); 0 < x; --x) 53 | if (0 <= i - x * x) 54 | dp[i] = Math.min(dp[i], 1 + dp[i - x * x]); // min squares x*x to reach sum i 🎯 55 | } 56 | return dp[N]; 57 | }; 58 | -------------------------------------------------------------------------------- /javascript/300_longest_increasing_subsequence.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 300. Longest Increasing Subsequence 3 | * 4 | * Q: https://leetcode.com/problems/longest-increasing-subsequence/ 5 | * A: https://leetcode.com/problems/longest-increasing-subsequence/discuss/385203/C%2B%2B-and-Javascript-solutions 6 | */ 7 | 8 | /** 9 | * @param {number[]} A 10 | * @return {number} 11 | */ 12 | 13 | let lengthOfLIS = (A, m = new Map()) => { 14 | let N = A.length; 15 | if (N == 0) 16 | return 0; 17 | let go = j => { 18 | if (m.has(j)) 19 | return m.get(j); 20 | m.set(j, 1); 21 | if (j == 0) 22 | return m.get(j); 23 | for (let i = 0; i < j; ++i) 24 | if (A[i] < A[j]) 25 | m.set(j, Math.max(m.get(j), 1 + go(i))); 26 | return m.get(j); 27 | }; 28 | [...Array(N).keys()].forEach((_, j) => go(j)); 29 | return Math.max(...m.values()); 30 | }; 31 | 32 | // let lengthOfLIS = (A, max = 1) => { 33 | // let N = A.length; 34 | // if (N == 0) 35 | // return 0; 36 | // let dp = Array(N).fill(1); 37 | // for (let j = 1; j < N; ++j) 38 | // for (let i = 0; i < j; ++i) 39 | // if (A[i] < A[j]) 40 | // max = Math.max(max, dp[j] = Math.max(dp[j], 1 + dp[i])); 41 | // return max; 42 | // }; 43 | 44 | var A = [1,3,6,7,9,4,10,5,6]; 45 | const ans = lengthOfLIS(A); 46 | console.log(ans); 47 | -------------------------------------------------------------------------------- /javascript/322_coin_change.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 322. Coin Change 3 | * 4 | * Q: https://leetcode.com/problems/coin-change/ 5 | * A: https://leetcode.com/problems/coin-change/discuss/677858/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // Top-Down: TLE 9 | let coinChange = (A, T) => { 10 | let N = A.length; 11 | let go = (i = 0, t = T) => { 12 | if (!t) 13 | return 0; 14 | if (i == N) 15 | return Infinity; 16 | return Math.min(1 + (0 <= t - A[i] ? go(i, t - A[i]) : Infinity), go(i + 1, t)); // min(1 + with, without) 17 | }; 18 | let ans = go(); 19 | return ans < Infinity ? ans : -1; 20 | }; 21 | 22 | // Top-Down with Memo: AC 23 | let coinChange = (A, T) => { 24 | let N = A.length; 25 | let m = [...Array(N + 1)].map(_ => Array(T + 1).fill(-1)); 26 | let go = (i = 0, t = T) => { 27 | if (m[i][t] > -1) 28 | return m[i][t]; 29 | if (!t) 30 | return m[i][t] = 0; 31 | if (i == N) 32 | return m[i][t] = Infinity; 33 | return m[i][t] = Math.min(1 + (0 <= t - A[i] ? go(i, t - A[i]) : Infinity), go(i + 1, t)); // min(1 + with or without) 34 | }; 35 | let ans = go(); 36 | return ans < Infinity ? ans : -1; 37 | }; 38 | 39 | // Bottom-Up: AC 40 | let coinChange = (A, T) => { 41 | let N = A.length; 42 | let dp = [...Array(N + 1)].map(_ => Array(T + 1).fill(Infinity)); 43 | for (let i = 0; i <= N; ++i) 44 | dp[i][0] = 0; 45 | for (let i = 1; i <= N; ++i) 46 | for (let t = 1; t <= T; ++t) 47 | dp[i][t] = Math.min(1 + (0 <= t - A[i - 1] ? dp[i][t - A[i - 1]] : Infinity), dp[i - 1][t]); // min(1 + with or without) 48 | return dp[N][T] < Infinity ? dp[N][T] : -1; 49 | }; 50 | 51 | // Bottom-Up Memory Optimized 1: AC 52 | let coinChange = (A, T) => { 53 | let N = A.length; 54 | let pre = Array(T + 1).fill(Infinity), 55 | cur = [...pre]; 56 | for (let i = 1; i <= N; ++i) { 57 | cur[0] = 0; 58 | for (let t = 1; t <= T; ++t) 59 | cur[t] = Math.min(1 + (0 <= t - A[i - 1] ? cur[t - A[i - 1]] : Infinity), pre[t]); // min(1 + with or without) 60 | [pre, cur] = [cur, pre]; 61 | } 62 | return pre[T] < Infinity ? pre[T] : -1; 63 | }; 64 | 65 | // Bottom-Up Memory Optimized 2: AC 66 | let coinChange = (A, T) => { 67 | let N = A.length; 68 | let dp = Array(T + 1).fill(Infinity); 69 | dp[0] = 0; 70 | for (let i = 1; i <= N; ++i) 71 | for (let t = 1; t <= T; ++t) 72 | dp[t] = Math.min(1 + (0 <= t - A[i - 1] ? dp[t - A[i - 1]] : Infinity), dp[t]); // min(1 + with or without) 73 | return dp[T] < Infinity ? dp[T] : -1; 74 | }; -------------------------------------------------------------------------------- /javascript/337_house_robber3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 337. House Robber III 3 | * 4 | * Q: https://leetcode.com/problems/house-robber-iii/ 5 | * A: https://leetcode.com/problems/house-robber-iii/discuss/946524/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // DFS 9 | let rob = root => { 10 | let go = (root, isRobbable = true) => { 11 | if (!root) 12 | return 0; 13 | let include = go(root.left, false) + go(root.right, false) + root.val, 14 | exclude = go(root.left, true) + go(root.right, true); 15 | return Math.max(isRobbable ? include : -Infinity, exclude); 16 | }; 17 | return go(root); 18 | }; 19 | 20 | // Memo 21 | let rob = (root, m = new Map()) => { 22 | let go = (root, i = 1, isRobbable = true) => { 23 | let key = `${root},${i},${isRobbable}`; 24 | if (m.has(key)) 25 | return m.get(key); 26 | if (!root) { 27 | m.set(key, 0); 28 | return m.get(key); 29 | } 30 | let L = 2 * i, 31 | R = 2 * i + 1; 32 | let include = go(root.left, L, false) + go(root.right, R, false) + root.val, 33 | exclude = go(root.left, L, true) + go(root.right, R, true); 34 | m.set(key, Math.max(isRobbable ? include : -Infinity, exclude)); 35 | return m.get(key); 36 | }; 37 | return go(root); 38 | }; 39 | -------------------------------------------------------------------------------- /javascript/416_can_partition_equal_subset_sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 416. Partition Equal Subset Sum 3 | * 4 | * Q: https://leetcode.com/problems/partition-equal-subset-sum/ 5 | * A: https://leetcode.com/problems/partition-equal-subset-sum/discuss/617275/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // top-down 9 | let canPartition = A => { 10 | let total = _.sum(A); 11 | if (total & 1) // ❌ odd total cannot be evenly divided by 2 12 | return false; 13 | let target = Math.floor(total / 2); 14 | let go = (i = 0, t = 0) => { 15 | if (i == A.length || target < t) // 🛑 base case: target not reached 16 | return false; 17 | if (t == target) // 🎯 target reached 18 | return true; 19 | return go(i + 1, t + A[i]) || go(i + 1, t); // ✅ with xor 🚫 without A[i] 20 | }; 21 | return go(); 22 | }; 23 | 24 | // memo 25 | let canPartition = (A, m = new Map()) => { 26 | let total = _.sum(A); 27 | if (total & 1) // ❌ odd total cannot be evenly divided by 2 28 | return false; 29 | let target = Math.floor(total / 2); 30 | let go = (i = 0, t = 0) => { 31 | let key = `${i},${t}`; 32 | if (m.has(key)) // 🤔 memo 33 | return m.get(key); 34 | if (i == A.length || target < t) // 🛑 base case: target not reached 35 | m.set(key, false); 36 | if (t == target) // 🎯 target reached 37 | m.set(key, true) 38 | if (!m.has(key)) 39 | m.set(key, go(i + 1, t + A[i]) || go(i + 1, t)); // ✅ with xor 🚫 without A[i] 40 | return m.get(key); 41 | }; 42 | return go(); 43 | }; 44 | 45 | // bottom-up 46 | let canPartition = A => { 47 | let total = _.sum(A); 48 | if (total & 1) // ❌ odd total cannot be evenly divided by 2 49 | return false; 50 | let target = Math.floor(total / 2); 51 | let dp = Array(target + 1).fill(0); // 🤔 memo 52 | dp[0] = 1; // 🛑 base case: we can reach target 0 53 | for (let x of A) // 🤔 if we can reach t 🚫 without x, then we can reach t ✅ with x 54 | for (let t = target; x <= t; --t) 55 | if (dp[t - x]) 56 | dp[t] = 1; 57 | return dp[target]; // 🎯 target reached? 58 | }; 59 | -------------------------------------------------------------------------------- /javascript/518_coin_change2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 518. Coin Change 2 3 | * 4 | * Q: https://leetcode.com/problems/coin-change-2/ 5 | * A: https://leetcode.com/problems/coin-change-2/discuss/677893/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // TopDown 9 | let change = (T, A) => { 10 | let N = A.length; 11 | let go = (i = 0, t = T) => { 12 | if (!t) 13 | return 1; 14 | if (i == N) 15 | return 0; 16 | return (0 <= t - A[i] ? go(i, t - A[i]) : 0) + go(i + 1, t); // with ✅ A[i] + without ❌ A[i] 17 | }; 18 | return go(); 19 | }; 20 | 21 | // TopDown with Memo 22 | let change = (T, A) => { 23 | let N = A.length; 24 | let m = [...Array(N + 1)].map(row => Array(T + 1).fill(-1)) 25 | let go = (i = 0, t = T) => { 26 | if (m[i][t] > -1) 27 | return m[i][t]; 28 | if (!t) 29 | return m[i][t] = 1; 30 | if (i == N) 31 | return m[i][t] = 0; 32 | return m[i][t] = (0 <= t - A[i] ? go(i, t - A[i]) : 0) + go(i + 1, t); // with ✅ A[i] + without ❌ A[i] 33 | }; 34 | let ans = go(); 35 | return ans; 36 | }; 37 | 38 | // BottomUp 39 | let change = (T, A) => { 40 | let N = A.length; 41 | let dp = [...Array(N + 1)].map(row => Array(T + 1).fill(0)); 42 | for (let i = 0; i <= N; ++i) 43 | dp[i][0] = 1; 44 | for (let i = 1; i <= N; ++i) 45 | for (let t = 1; t <= T; ++t) 46 | dp[i][t] = (0 <= t - A[i - 1] ? dp[i][t - A[i - 1]] : 0) + dp[i - 1][t]; // with ✅ A[i] + without ❌ A[i] 47 | return dp[N][T]; 48 | }; 49 | 50 | // BottomUp MemOpt1 51 | let change = (T, A) => { 52 | let N = A.length; 53 | let pre = Array(T + 1).fill(0), 54 | cur = [...pre]; 55 | pre[0] = 1; 56 | for (let i = 1; i <= N; ++i) { 57 | cur[0] = 1; 58 | for (let t = 1; t <= T; ++t) 59 | cur[t] = (0 <= t - A[i - 1] ? cur[t - A[i - 1]] : 0) + pre[t]; // with ✅ A[i] + without ❌ A[i] 60 | [pre, cur] = [cur, pre]; 61 | } 62 | return pre[T]; 63 | }; 64 | 65 | // BottomUp MemOpt2 66 | let change = (T, A) => { 67 | let N = A.length; 68 | let dp = Array(T + 1).fill(0); 69 | dp[0] = 1; 70 | for (let i = 1; i <= N; ++i) 71 | for (let t = 1; t <= T; ++t) 72 | dp[t] += 0 <= t - A[i - 1] ? dp[t - A[i - 1]] : 0; // with ✅ A[i] + without ❌ A[i] 73 | return dp[T]; 74 | }; -------------------------------------------------------------------------------- /javascript/5_longest_palindromic_substring.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 5. Longest Palindromic Substring 3 | * 4 | * Q: https://leetcode.com/problems/longest-palindromic-substring/ 5 | * A: https://leetcode.com/problems/longest-palindromic-substring/discuss/635659/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // top-down TLE without memo 9 | // "babaddtattarrattatddetartrateedredividerb" 10 | /* 11 | let longestPalindrome = s => { 12 | let N = s.length; 13 | if (!N) return ''; 14 | let ans = s[0]; 15 | let best = (i, j) => { 16 | if (ans.length < j - i + 1) // +1 for i..j inclusive 17 | ans = s.substring(i, j + 1); // +1 for i..j inclusive 🎯 18 | }; 19 | let go = (i = 0, j = N - 1) => { 20 | if (j - i < 2) { 21 | if (s[i] == s[j]) { 22 | best(i, j); 23 | return true; 24 | } 25 | return false; 26 | } 27 | go(i + 1, j); // case 1: the sub-problem without the character at index i 28 | go(i, j - 1); // case 2: the sub-problem without the character at index j 29 | if (go(i + 1, j - 1)) { // case 3: the sub-problem without the character at index i and j 30 | if (s[i] == s[j]) { 31 | best(i, j); 32 | return true; 33 | } 34 | } 35 | return false; 36 | }; 37 | go(); 38 | return ans; 39 | }; 40 | */ 41 | 42 | // top-down TLE with memo 43 | // "anugnxshgonmqydttcvmtsoaprxnhpmpovdolbidqiyqubirkvhwppcdyeouvgedccipsvnobrccbndzjdbgxkzdbcjsjjovnhpnbkurxqfupiprpbiwqdnwaqvjbqoaqzkqgdxkfczdkznqxvupdmnyiidqpnbvgjraszbvvztpapxmomnghfaywkzlrupvjpcvascgvstqmvuveiiixjmdofdwyvhgkydrnfuojhzulhobyhtsxmcovwmamjwljioevhafdlpjpmqstguqhrhvsdvinphejfbdvrvabthpyyphyqharjvzriosrdnwmaxtgriivdqlmugtagvsoylqfwhjpmjxcysfujdvcqovxabjdbvyvembfpahvyoybdhweikcgnzrdqlzusgoobysfmlzifwjzlazuepimhbgkrfimmemhayxeqxynewcnynmgyjcwrpqnayvxoebgyjusppfpsfeonfwnbsdonucaipoafavmlrrlplnnbsaghbawooabsjndqnvruuwvllpvvhuepmqtprgktnwxmflmmbifbbsfthbeafseqrgwnwjxkkcqgbucwusjdipxuekanzwimuizqynaxrvicyzjhulqjshtsqswehnozehmbsdmacciflcgsrlyhjukpvosptmsjfteoimtewkrivdllqiotvtrubgkfcacvgqzxjmhmmqlikrtfrurltgtcreafcgisjpvasiwmhcofqkcteudgjoqqmtucnwcocsoiqtfuoazxdayricnmwcg" 44 | /* 45 | let longestPalindrome = (s, m = new Map()) => { 46 | let N = s.length; 47 | if (!N) 48 | return ''; 49 | let max = 1, 50 | ans = s[0]; 51 | let best = (i, j) => { 52 | if (max < j - i + 1) { 53 | max = j - i + 1; // +1 for i..j inclusive 54 | ans = s.substring(i, j + 1); // +1 for j non-inclusive 55 | } 56 | }; 57 | let go = (i = 0, j = N - 1) => { 58 | if (m.has(`${i},${j}`)) 59 | return m.get(`${i},${j}`); 60 | if (i + 1 >= j) { 61 | if (s[i] == s[j]) { 62 | best(i, j); 63 | m.set(`${i},${j}`, true); 64 | return true; 65 | } 66 | m.set(`${i},${j}`, false); 67 | return false; 68 | } 69 | go(i + 1, j); 70 | go(i, j - 1); 71 | if (go(i + 1, j - 1)) { 72 | if (s[i] == s[j]) { 73 | best(i, j); 74 | m.set(`${i},${j}`, true); 75 | return true; 76 | } 77 | } 78 | m.set(`${i},${j}`, false); 79 | return false; 80 | }; 81 | go(); 82 | return ans; 83 | }; 84 | */ 85 | 86 | // brute-force bottom up: AC 87 | /* 88 | let longestPalindrome = (s, max = 1, ans = '') => { 89 | let N = s.length; 90 | if (!N) return ans; 91 | ans = s[0]; 92 | let expand = (i, j) => { 93 | for (; 0 <= i && j < N && s[i] == s[j]; --i, ++j) 94 | if (max < j - i + 1) 95 | max = j - i + 1, ans = s.substring(i, j + 1); // +1 for i..j inclusive 🎯 96 | }; 97 | for (let i = 0; i + 1 < N; ++i) 98 | expand(i, i), // case 1: odd length palindrome 99 | expand(i, i + 1); // case 2: even length palindrome 100 | return ans; 101 | }; 102 | */ 103 | 104 | // bottom-up DP: AC 105 | let longestPalindrome = s => { 106 | let N = s.length; 107 | if (!N) return ''; 108 | let ans = s[0]; 109 | let dp = [...Array(N)].map(row => Array(N).fill(false)); 110 | for (let j = 1; j < N; ++j) { 111 | for (let i = j; i >= 0; --i) { 112 | dp[i][j] = s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]); 113 | if (dp[i][j] && ans.length < j - i + 1) 114 | ans = s.substring(i, j + 1); // +1 for i..j inclusive 🎯 115 | } 116 | } 117 | return ans; 118 | }; 119 | 120 | console.log(longestPalindrome('a')); 121 | console.log(longestPalindrome('ac')); 122 | console.log(longestPalindrome('bb')); 123 | console.log(longestPalindrome('babad')); 124 | console.log(longestPalindrome('cbbd')); 125 | console.log(longestPalindrome('babaddtattarrattatddetartrateedredividerb')); 126 | console.log(longestPalindrome('anugnxshgonmqydttcvmtsoaprxnhpmpovdolbidqiyqubirkvhwppcdyeouvgedccipsvnobrccbndzjdbgxkzdbcjsjjovnhpnbkurxqfupiprpbiwqdnwaqvjbqoaqzkqgdxkfczdkznqxvupdmnyiidqpnbvgjraszbvvztpapxmomnghfaywkzlrupvjpcvascgvstqmvuveiiixjmdofdwyvhgkydrnfuojhzulhobyhtsxmcovwmamjwljioevhafdlpjpmqstguqhrhvsdvinphejfbdvrvabthpyyphyqharjvzriosrdnwmaxtgriivdqlmugtagvsoylqfwhjpmjxcysfujdvcqovxabjdbvyvembfpahvyoybdhweikcgnzrdqlzusgoobysfmlzifwjzlazuepimhbgkrfimmemhayxeqxynewcnynmgyjcwrpqnayvxoebgyjusppfpsfeonfwnbsdonucaipoafavmlrrlplnnbsaghbawooabsjndqnvruuwvllpvvhuepmqtprgktnwxmflmmbifbbsfthbeafseqrgwnwjxkkcqgbucwusjdipxuekanzwimuizqynaxrvicyzjhulqjshtsqswehnozehmbsdmacciflcgsrlyhjukpvosptmsjfteoimtewkrivdllqiotvtrubgkfcacvgqzxjmhmmqlikrtfrurltgtcreafcgisjpvasiwmhcofqkcteudgjoqqmtucnwcocsoiqtfuoazxdayricnmwcg')); 127 | -------------------------------------------------------------------------------- /javascript/62_unique_paths.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 62. Unique Paths 3 | * 4 | * Q: https://leetcode.com/problems/unique-paths/ 5 | * A: https://leetcode.com/problems/unique-paths/discuss/22965/c-top-down-recursion-and-bottom-up-dp/501132 6 | */ 7 | 8 | // let uniquePaths = (M, N, m = {}) => { 9 | // m[`0,0`] = 1; 10 | // let go = (i, j) => { 11 | // let k = `${i},${j}` 12 | // if (m[k]) 13 | // return m[k]; 14 | // if (i == 0 || j == 0) 15 | // return m[k] = 1; 16 | // return m[k] = go(i - 1, j) + go(i, j - 1); 17 | // }; 18 | // go(M, N); 19 | // return m[`${M - 1},${N - 1}`]; 20 | // }; 21 | 22 | // let uniquePaths = (M, N, m = {}) => { 23 | // let dp = [...Array(M)].map(row => Array(N).fill(1)); 24 | // for (let i = 1; i < M; ++i) 25 | // for (let j = 1; j < N; ++j) 26 | // dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 27 | // return dp[M - 1][N - 1]; 28 | // }; 29 | 30 | let uniquePaths = (M, N, m = {}) => { 31 | let pre = Array(N).fill(1), 32 | cur = Array(N).fill(1); 33 | for (let i = 1; i < M; ++i) { 34 | for (let j = 1; j < N; ++j) 35 | cur[j] = pre[j] + cur[j - 1]; 36 | [pre, cur] = [cur, pre]; 37 | } 38 | return pre[N - 1]; 39 | }; -------------------------------------------------------------------------------- /javascript/673_max_LIS.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 673. Number of Longest Increasing Subsequence 3 | * 4 | * Q: https://leetcode.com/problems/number-of-longest-increasing-subsequence/ 5 | * A: https://leetcode.com/problems/number-of-longest-increasing-subsequence/discuss/916696/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | let findNumberOfLIS = (A, length = 0, best = 0) => { 9 | let N = A.length; 10 | let dp = Array(N).fill(1), 11 | cnt = Array(N).fill(1); 12 | for (let j = 0; j < N; ++j) { 13 | for (let i = 0; i < j; ++i) { 14 | if (A[i] < A[j]) { 15 | if (dp[j] < 1 + dp[i]) 16 | dp[j] = 1 + dp[i], 17 | cnt[j] = 0; 18 | if (dp[j] == 1 + dp[i]) 19 | cnt[j] += cnt[i]; 20 | } 21 | } 22 | if (length < dp[j]) 23 | length = dp[j], 24 | best = 0; 25 | if (length == dp[j]) 26 | best += cnt[j]; 27 | } 28 | return best; 29 | }; 30 | -------------------------------------------------------------------------------- /javascript/72_edit_distance.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 72. Edit Distance 3 | * 4 | * Q: https://leetcode.com/problems/edit-distance/ 5 | * A: https://leetcode.com/problems/edit-distance/discuss/479377/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | /** 9 | * @param {string} word1 10 | * @param {string} word2 11 | * @return {number} 12 | */ 13 | 14 | /* 15 | let minDistance = (A, B) => { 16 | let go = (A, B, i, j) => { 17 | let key = `${i},${j}`; 18 | if (i == 0 || j == 0) 19 | return i + j; 20 | return Math.min( 21 | go(A, B, i - 1, j - 1) + Number(A[i - 1] != B[j - 1]), 22 | go(A, B, i - 1, j) + 1, 23 | go(A, B, i, j - 1) + 1 24 | ); 25 | }; 26 | return go(A, B, A.length, B.length); 27 | }; 28 | */ 29 | 30 | /* 31 | let minDistance = (A, B) => { 32 | let go = (A, B, i, j, memo = {}) => { 33 | let key = `${i},${j}`; 34 | if (memo[key]) 35 | return memo[key]; 36 | if (i == 0 || j == 0) 37 | return memo[key] = i + j; 38 | return memo[key] = Math.min( 39 | go(A, B, i - 1, j - 1, memo) + Number(A[i - 1] != B[j - 1]), 40 | go(A, B, i - 1, j, memo) + 1, 41 | go(A, B, i, j - 1, memo) + 1 42 | ); 43 | }; 44 | return go(A, B, A.length, B.length); 45 | }; 46 | */ 47 | 48 | /* 49 | let minDistance = (A, B) => { 50 | let [M, N] = [A.length, B.length]; 51 | let dp = [...Array(M + 1)].map(row => Array(N + 1).fill(0)); 52 | for (let i = 0; i <= M; ++i) dp[i][0] = i; 53 | for (let j = 0; j <= N; ++j) dp[0][j] = j; 54 | for (let i = 1; i <= M; ++i) 55 | for (let j = 1; j <= N; ++j) 56 | dp[i][j] = Math.min( 57 | dp[i - 1][j - 1] + Number(A[i - 1] != B[j - 1]), 58 | dp[i - 1][j] + 1, 59 | dp[i][j - 1] + 1, 60 | ); 61 | return dp[M][N]; 62 | }; 63 | */ 64 | 65 | let minDistance = (A, B) => { 66 | let [M, N] = [A.length, B.length]; 67 | let pre = [...Array(N + 1).keys()], cur = Array(N + 1); 68 | for (let i = 1; i <= M; ++i) { 69 | cur[0] = i; 70 | for (let j = 1; j <= N; ++j) { 71 | cur[j] = Math.min( 72 | pre[j - 1] + Number(A[i - 1] != B[j - 1]), 73 | pre[j] + 1, 74 | cur[j - 1] + 1, 75 | ); 76 | } 77 | [pre, cur] = [cur, pre]; 78 | } 79 | return pre[N]; 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /javascript/746_min_cost_climbing_stairs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 746. Min Cost Climbing Stairs 3 | * 4 | * Q: https://leetcode.com/problems/min-cost-climbing-stairs/ 5 | * A: https://leetcode.com/problems/min-cost-climbing-stairs/discuss/110111/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // brute-force top-down DFS 9 | let minCostClimbingStairs = A => { 10 | let N = A.length; 11 | let go = i => { 12 | if (N <= i) 13 | return 0; // 🛑 base case: we reached the top floor 14 | return A[i] + Math.min(go(i + 1), go(i + 2)); // cost of i-th stair plus min(one step or two steps) 🎯 15 | }; 16 | return Math.min(go(0), go(1)); // start on min(first step, second step) 17 | }; 18 | 19 | // top-down with memo 20 | let minCostClimbingStairs = (A, m = {}) => { 21 | let N = A.length; 22 | let go = i => { 23 | if (m[i]) 24 | return m[i]; // memo 🤔 25 | if (N <= i) 26 | return m[i] = 0; // 🛑 base case: we reached the top floor 27 | return m[i] = A[i] + Math.min(go(i + 1), go(i + 2)); // cost of i-th stair plus min(one step or two steps) 🎯 28 | }; 29 | return Math.min(go(0), go(1)); // start on min(first step, second step) 30 | }; 31 | 32 | // bottom-up 33 | let minCostClimbingStairs = A => { 34 | let N = A.length; 35 | let dp = Array(N); 36 | dp[0] = A[0]; // 🛑 base case: start on first step 37 | dp[1] = A[1]; // 🛑 base case: start on second step 38 | for (let i = 2; i < N; ++i) 39 | dp[i] = A[i] + Math.min(dp[i - 2], dp[i - 1]); // cost of i-th stair plus min(one step or two steps) 🎯 40 | return Math.min(dp[N - 2], dp[N - 1]); // N-th stair is reached from min of one or two stairs away 41 | }; 42 | 43 | // bottom-up memory optimized 44 | let minCostClimbingStairs = A => { 45 | let N = A.length; 46 | let a = A[0], // 🛑 base case: start on first step 47 | b = A[1], // 🛑 base case: start on second step 48 | c = -1; 49 | for (let i = 2; i < N; ++i, a = b, b = c) 50 | c = A[i] + Math.min(a, b); // cost of i-th stair plus min(one step or two steps) 🎯 51 | return Math.min(a, b); // N-th stair is reached from min of one or two stairs away 52 | }; 53 | -------------------------------------------------------------------------------- /javascript/787_cheapest_flights_within_k_stops.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 787. Cheapest Flights Within K Stops 3 | * 4 | * Q: https://leetcode.com/problems/cheapest-flights-within-k-stops/ 5 | * A: https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/690997/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // DFS + BT 9 | let findCheapestPrice = (_, E, start, T, hops, adj = new Map(), best = Infinity) => { 10 | E.forEach(([u, v, w]) => adj.set(u, (adj.get(u) || new Set()).add([v, w]))); 11 | let go = (u = start, k = hops + 1, cost = 0, seen = new Set()) => { 12 | if (u == T) 13 | best = Math.min(best, cost); // best is the minimum cost 🎯 14 | if (u == T || !k) 15 | return; // destination reached or k-hops exhausted: stop 🛑 16 | seen.add(u); // 👀 ✅ forward-tracking 17 | for (let [v, w] of [...adj.get(u) || []]) 18 | if (!seen.has(v) && cost + w < best) // pruning condition: cost + w < best 🤔 19 | go(v, k - 1, cost + w, seen); 20 | seen.delete(u); // 👀 ❌ back-tracking 21 | }; 22 | go(); 23 | return best < Infinity ? best : -1; 24 | }; 25 | 26 | // DFS 27 | let findCheapestPrice = (N, A, start, T, hops, adj = new Map()) => { 28 | A.forEach(([u, v, w]) => adj.set(u, (adj.get(u) || new Set()).add([v, w]))); 29 | let go = (u = start, k = hops + 1, min = Infinity) => { 30 | if (u == T) 31 | return 0; // target T 🎯 32 | if (!k) 33 | return Infinity; // all k hops exhausted ❌ 34 | for (let [v, w] of [...adj.get(u) || []]) 35 | min = Math.min(min, w + go(v, k - 1)); // dfs edge u -> v with cost w 36 | return min; 37 | }; 38 | return go() < Infinity ? go() : -1; 39 | }; 40 | 41 | // DFS with Memo 42 | let findCheapestPrice = (N, E, start, T, hops, adj = new Map()) => { 43 | let m = [...Array(N)].map(_ => Array(hops + 2).fill(-1)); // +2 because for V vertices there are V+1 edges and +1 for the memo itself to be 0..hops+1 inclusive 44 | E.forEach(([u, v, w]) => adj.set(u, (adj.get(u) || new Set()).add([v, w]))); 45 | let go = (u = start, k = hops + 1, min = Infinity) => { 46 | if (m[u][k] > -1) 47 | return m[u][k]; // memo 🤔 48 | if (u == T) 49 | return m[u][k] = 0; // target T 🎯 50 | if (!k) 51 | return m[u][k] = Infinity; // all k hops exhausted ❌ 52 | for (let [v, w] of [...adj.get(u) || []]) 53 | min = Math.min(min, w + go(v, k - 1)); // dfs edge u -> v with cost w 54 | return m[u][k] = min; 55 | }; 56 | return go() < Infinity ? go() : -1; 57 | }; 58 | 59 | // BF 60 | let findCheapestPrice = (N, A, start, T, hops) => { 61 | let k = hops + 1; 62 | let pre = Array(N).fill(Infinity); 63 | pre[start] = 0; 64 | while (k--) { // relax all edges k times 65 | // bellman-ford: dist[v] = min(dist[v], dist[u] + w) ie. relax edge u,v of cost w 66 | // however, use previous and current to avoid overwritting our previous optimal edge relaxations 67 | // which we still need to read from in order to derive our current optimal edge relaxations for each k-th hop 68 | let cur = [...pre]; // derive current from previous 🤔 ie. update current optimal edge relaxations based upon previous optimal edge relaxations 69 | A.forEach(([u, v, w]) => cur[v] = Math.min(cur[v], pre[u] + w)); // relax all edges u,v of cost w for optimal distance to v 🎯 70 | [pre, cur] = [cur, pre]; // persist current as previous via swap 🤔 71 | } 72 | return pre[T] < Infinity ? pre[T] : -1; 73 | } 74 | 75 | // SPFA 76 | let findCheapestPrice = (N, E, start, T, hops, adj = new Map()) => { 77 | E.forEach(([u, v, w]) => adj.set(u, (adj.get(u) || new Set()).add([v, w]))); 78 | let k = hops + 1; 79 | let pre = Array(N).fill(Infinity); 80 | pre[start] = 0; 81 | let q = [ start ]; 82 | while (k--) { 83 | let cur = [...pre]; 84 | let len = q.length; 85 | while (len--) { 86 | let u = q.shift(); 87 | for (let [v, w] of [...adj.get(u) || []]) 88 | if (cur[v] > pre[u] + w) 89 | cur[v] = pre[u] + w, q.push(v); 90 | } 91 | [pre, cur] = [cur, pre]; 92 | } 93 | return pre[T] < Infinity ? pre[T] : -1; 94 | } 95 | 96 | console.log(findCheapestPrice(3, [[0,1,2],[1,2,1],[2,0,10]], 1, 2, 1)); 97 | console.log(findCheapestPrice(4, [[0,1,1],[0,2,5],[1,2,1],[2,3,1]], 0, 3, 1)); 98 | -------------------------------------------------------------------------------- /javascript/799_champagne_tower.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 799. Champagne Tower 3 | * 4 | * Q: https://leetcode.com/problems/champagne-tower/ 5 | * A: https://leetcode.com/problems/champagne-tower/discuss/118694/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // top-down with memo 9 | let champagneTower = (K, M, N, m = new Map()) => { 10 | let go = (i, j) => { 11 | let key = `${i},${j}`; 12 | if (m.has(key)) 13 | return m.get(key); // 🤔 memo 14 | else if (!i && !j) 15 | m.set(key, K); // 🛑 base case: glass at row 0 column 0 has K poured through it 16 | else if (!i || j < 0) 17 | m.set(key, 0.0); // 🚫 non-existent parent glass has 0.0 poured through it 18 | else { 19 | // ⭐️ each parent glass above-and-to-the-(L)eft/(R)ight either overflow when the amount poured exceeds 1.0 xor do *not* overflow when the amount poured does *not* exceed 1.0 20 | // 💎 -1.0 since parent glass above consumes at-most 1.0 of the pour and div 2 when overflow occurs, because half overflows on each side of the parent glass 21 | let L = go(i - 1, j - 1), 22 | R = go(i - 1, j); 23 | m.set(key, (1.0 <= L ? (L - 1.0) / 2 : 0.0) + (1.0 <= R ? (R - 1.0) / 2 : 0.0)); 24 | } 25 | return m.get(key); 26 | }; 27 | go(M, Math.max(M, N)); // 🌟 since the glasses above-and-to-the-right potentially contribute to the amount poured to M, N we choose N to be the maximum of M, N 28 | return Math.min(go(M, N), 1.0); 29 | }; 30 | 31 | // bottom-up 32 | let champagneTower = (K, M, N) => { 33 | let dp = [...Array(M + 1)].map(_ => Array(N + 2).fill(0)); 34 | dp[0][0] = K; 35 | for (let i = 0; i < M; ++i) { 36 | for (let j = 0; j <= N; ++j) { 37 | if (dp[i][j] <= 1.0) // no overflow 38 | continue; 39 | let half = (dp[i][j] - 1.0) / 2; // -1.0 to fill cup i,j 40 | dp[i + 1][j] += half; 41 | dp[i + 1][j + 1] += half; 42 | } 43 | } 44 | return Math.min(dp[M][N], 1.0); 45 | }; 46 | 47 | 48 | // memory optimized 49 | let champagneTower = (K, M, N) => { 50 | let pre = Array(N + 2).fill(0.0); 51 | pre[0] = K; 52 | while (M--) { 53 | let cur = Array(N + 2).fill(0.0); 54 | for (let j = 0; j <= N; ++j) { 55 | if (pre[j] <= 1.0) // no overflow 56 | continue; 57 | let half = (pre[j] - 1.0) / 2; // -1.0 to fill cup i,j 58 | cur[j] += half; 59 | cur[j + 1] += half; 60 | } 61 | [pre, cur] = [cur, pre]; 62 | } 63 | return Math.min(pre[N], 1.0); 64 | }; 65 | -------------------------------------------------------------------------------- /javascript/877_stone_game.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 877. Stone Game 3 | * 4 | * Q: https://leetcode.com/problems/stone-game/ 5 | * A: https://leetcode.com/problems/stone-game/discuss/706734/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // top-down brute-force 9 | let stoneGame = A => { 10 | let N = A.length; 11 | let go = (i = 0, j = N - 1) => { 12 | if (i == j) 13 | return A[i]; // first == last 🛑 14 | return Math.max(A[i] - go(i + 1, j), A[j] - go(i, j - 1)); // max(first, last) 🎯 15 | }; 16 | return 0 < go(); 17 | }; 18 | 19 | // top-down memo 20 | let stoneGame = A => { 21 | let N = A.length; 22 | let m = [...Array(N)].map(_ => Array(N).fill(0)); 23 | let go = (i = 0, j = N - 1) => { 24 | if (m[i][j]) 25 | return m[i][j]; // memo 🤔 26 | if (i == j) 27 | return m[i][j] = A[i]; // first == last 🛑 28 | let ans = m[i][j] = Math.max(A[i] - go(i + 1, j), A[j] - go(i, j - 1)); // max(first, last) 🎯 29 | return ans; 30 | }; 31 | return 0 < go(); 32 | }; 33 | 34 | // bottom-up 35 | let stoneGame = A => { 36 | let N = A.length; 37 | let dp = [...Array(N)].map(_ => Array(N).fill(0)); // memo 🤔 38 | for (let i = N - 1; 0 <= i; --i) 39 | dp[i][i] = A[i]; // first == last 🛑 40 | for (let i = N - 1; 0 <= i; --i) // i-th first stone 41 | for (let j = i + 1; j < N; ++j) // j-th last stone 42 | dp[i][j] = Math.max(dp[i][i] - dp[i + 1][j], dp[j][j] - dp[i][j - 1]); // max(first, last) 🎯 43 | return 0 < dp[0][N - 1]; 44 | }; 45 | 46 | // bottom-up optimized 47 | let stoneGame = A => { 48 | let N = A.length; 49 | let dp = [...Array(N)].map(_ => Array(N).fill(0)); // memo 🤔 50 | for (let i = N - 1; 0 <= i; --i) // i-th first stone 51 | for (let j = i + 1; j < N; ++j) // j-th last stone 52 | dp[i][j] = Math.max(A[i] - dp[i + 1][j], A[j] - dp[i][j - 1]); // max(first, last) 🎯 53 | return 0 < dp[0][N - 1]; 54 | }; 55 | -------------------------------------------------------------------------------- /javascript/91_decode_ways.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 91. Decode Ways 3 | * 4 | * Q: https://leetcode.com/problems/decode-ways/ 5 | * A: https://leetcode.com/problems/decode-ways/discuss/117143/Kt-Js-Py3-Cpp-The-ART-of-Dynamic-Programming 6 | */ 7 | 8 | // top-down 9 | let numDecodings = s => { 10 | let N = s.length; 11 | let ok = x => 1 <= x && x <= 26; 12 | let go = i => { 13 | if (i == N) 14 | return 1; 15 | let cnt = 0, 16 | one = Number(s[i]), 17 | two = one && i + 1 < N ? Number(s[i] + s[i + 1]) : 0; 18 | if (ok(one)) cnt += go(i + 1); 19 | if (ok(two)) cnt += go(i + 2); 20 | return cnt; 21 | }; 22 | return go(0); 23 | }; 24 | 25 | // top-down with memo 26 | let numDecodings = (s, m = new Map()) => { 27 | let N = s.length; 28 | let ok = x => 1 <= x && x <= 26; 29 | let go = i => { 30 | if (m.has(i)) 31 | return m.get(i); 32 | if (i == N) 33 | return 1; 34 | let cnt = 0, 35 | one = Number(s[i]), 36 | two = one && i + 1 < N ? Number(s[i] + s[i + 1]) : 0; 37 | if (ok(one)) cnt += go(i + 1); 38 | if (ok(two)) cnt += go(i + 2); 39 | return m.set(i, cnt).get(i); 40 | }; 41 | return go(0); 42 | }; 43 | 44 | // bottom-up 45 | let numDecodings = s => { 46 | let N = s.length; 47 | let dp = Array(N + 2).fill(0); 48 | dp[N] = 1; 49 | let ok = x => 1 <= x && x <= 26; 50 | for (let i = N - 1; 0 <= i; --i) { 51 | let one = Number(s[i]), 52 | two = one && i + 1 <= N ? Number(s[i] + s[i + 1]) : 0; 53 | if (ok(one)) dp[i] += dp[i + 1]; 54 | if (ok(two)) dp[i] += dp[i + 2]; 55 | } 56 | return dp[0]; 57 | }; 58 | 59 | // bottom-up mem-opt 60 | let numDecodings = s => { 61 | let N = s.length, 62 | a = 0, 63 | b = 1, 64 | c = 0; 65 | let ok = x => 1 <= x && x <= 26; 66 | for (let i = N - 1; 0 <= i; --i) { 67 | let one = Number(s[i]), 68 | two = one && i + 1 <= N ? Number(s[i] + s[i + 1]) : 0; 69 | a = 0; 70 | if (ok(one)) a += b; 71 | if (ok(two)) a += c; 72 | c = b, b = a; 73 | } 74 | return a; 75 | }; 76 | -------------------------------------------------------------------------------- /javascript/96_unique_binary_search_trees.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 96. Unique Binary Search Trees 3 | * 4 | * Q: https://leetcode.com/problems/unique-binary-search-trees/ 5 | * A: https://leetcode.com/problems/unique-binary-search-trees/discuss/703865/Javascript-and-C%2B%2B-solutions 6 | */ 7 | 8 | // brute-force top-down DFS 9 | let numTrees = N => { 10 | let go = (i = 1, j = N, ans = 0) => { 11 | if (j <= i) 12 | return 1; // 🛑 1 unique way to create: empty tree (if j < i) xor tree with 1 node (if j == i) 13 | for (let k = i; k <= j; ++k) // for each root node k from i..j inclusive 14 | ans += go(i, k - 1) * go(k + 1, j); // 🎯 ans = left subtree ans * right subtree ans 15 | return ans; 16 | }; 17 | return go(); 18 | }; 19 | 20 | // top-down DFS with memo 21 | let numTrees = N => { 22 | let m = [...Array(N + 2)].map(_ => Array(N + 2).fill(0)); // +2 for 1..N+1 inclusive 23 | let go = (i = 1, j = N, ans = 0) => { 24 | if (m[i][j]) 25 | return m[i][j]; // memo 🤔 26 | if (j <= i) 27 | return m[i][j] = 1; // 🛑 1 unique way to create: empty tree (if j < i) xor tree with 1 node (if j == i) 28 | for (let k = i; k <= j; ++k) // for each root node k from i..j inclusive 29 | ans += go(i, k - 1) * go(k + 1, j); // 🎯 ans = left subtree ans * right subtree ans 30 | return m[i][j] = ans; 31 | }; 32 | return go(); 33 | }; 34 | 35 | // bottom-up 36 | let numTrees = N => { 37 | let dp = Array(N + 1).fill(0); // +1 for 1..N inclusive 38 | dp[0] = dp[1] = 1; // 🛑 1 unique way to create: empty tree xor tree with 1 node 39 | for (let j = 2; j <= N; ++j) // for each tree with j nodes 40 | for (let k = 1; k <= j; ++k) // for each root node k from 1..j inclusive 41 | dp[j] += dp[k - 1] * dp[j - k]; // 🎯 ans = left subtree ans * right subtree ans 42 | return dp[N]; 43 | }; 44 | -------------------------------------------------------------------------------- /javascript/983_min_cost_tickets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 983. Minimum Cost For Tickets 3 | * 4 | * Q: https://leetcode.com/problems/minimum-cost-for-tickets/ 5 | * A: https://leetcode.com/problems/minimum-cost-for-tickets/discuss/811237/Javascript-Python3-C%2B%2B-Top-Down-%2B-Bottom-Up 6 | */ 7 | 8 | // top-down 9 | let mincostTickets = (A, cost, days = [1, 7, 30]) => { 10 | let N = A.length; 11 | let go = (i = 0, day = 0) => { 12 | while (i < N && A[i] < day) 13 | ++i; 14 | if (i == N) 15 | return 0; // 🛑 base case 16 | let min = Infinity; 17 | for (let k = 0; k < 3; ++k) 18 | min = Math.min(min, cost[k] + go(i, A[i] + days[k])); // 🎯 min cost 19 | return min; 20 | }; 21 | return go(); 22 | }; 23 | 24 | // top-down memo 25 | let mincostTickets = (A, cost, days = [1, 7, 30], m = {}) => { 26 | let N = A.length; 27 | let go = (i = 0, day = 0) => { 28 | while (i < N && A[i] < day) 29 | ++i; 30 | if (i == N) 31 | return 0; // 🛑 base case 32 | if (m[i]) 33 | return m[i]; // 🤔 memo 34 | m[i] = Infinity; 35 | for (let k = 0; k < 3; ++k) 36 | m[i] = Math.min(m[i], cost[k] + go(i, A[i] + days[k])); // 🎯 min cost 37 | return m[i]; 38 | }; 39 | return go(); 40 | }; 41 | 42 | // bottom-up 43 | let mincostTickets = (A, cost, days = [1, 7, 30]) => { 44 | let N = A.length; 45 | let dp = Array(N + 1).fill(Infinity); // 🤔 memo 46 | dp[N] = 0; // 🛑 base case 47 | for (let i = N - 1; 0 <= i; --i) { 48 | let j = i; 49 | for (let k = 0; k < 3; ++k) { 50 | while (j < N && A[j] < A[i] + days[k]) 51 | ++j; 52 | dp[i] = Math.min(dp[i], cost[k] + dp[j]) // 🎯 min cost 53 | } 54 | } 55 | return dp[0]; 56 | } 57 | --------------------------------------------------------------------------------