{"componentChunkName":"component---src-templates-article-js","path":"/a-tour-around-competitive-programming","result":{"data":{"markdownRemark":{"html":"<p>During the past year, I've been exploring the \"competitive programming problems\" world. It all started around October 2019, when I began studying for coding interviews at Amazon, Bending Spoons and, most of all, Palantir Technologies.</p>\n<p>I wanted to have a somewhat methodic and organized way of tracking my progress, while at the same time trying to keep focus on problem types that were common in large tech companies' interviews. After trying out <a href=\"https://leetcode.com/\">LeetCode</a>, <a href=\"https://projecteuler.net/\">Project Euler</a> and <a href=\"https://www.hackerrank.com\">Hackerrank</a>, I particularly enjoyed the former, since most of the community seemed to have the same focus as I did. I started a <a href=\"https://github.com/xRuiAlves/leetcode\">GitHub repository</a> featuring all my solution proposals, which were developed using <code>Java</code>.</p>\n<p>As of the start of April, when I finished my interview process, I had solved <strong>41 easy</strong>, <strong>119 medium</strong> and <strong>8 hard</strong> problems. I found that the \"repetitive\" process of solving similar problems (that is, problems that have analogous solutions) resulted in training my brain to quickly recognize how to solve a new problem, whether by identifying the most appropriate data structures for the given scenario or by applying the correct solving methodologies (dynamic programming, memoization techniques, etc).</p>\n<p>In the last 4 months I've been less active, although I've been tackling problems from the <a href=\"https://leetcode.com/explore/other/card/30-day-leetcoding-challenge\">LeetCode monthly challenges</a> every day, which started back in April. During this time, I also participated in a couple of contests, although I didn't do particularly well in either of them.</p>\n<p>Although there were many problems I enjoyed solving, I decided to pick two of them to share (not because they were particularly my favorites, but because they had something interesting by nature).</p>\n<h2>Problem 72 - Edit Distance [Hard]</h2>\n<pre><code>Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2. \nYou have the following 3 operations permitted on a word:\n- Insert a character\n- Delete a character\n- Replace a character\n</code></pre>\n<p>I had implemented some <code>C++</code> code in the past using a \"known\" algorithm to compute two strings' edit distance. However, I wanted to try finding a solution by myself, without recurring to the literature (to simulate an interview-like situation). Based on the operations described in the formulation, this type of edit distance is also known as <a href=\"https://en.wikipedia.org/wiki/Levenshtein_distance\">Levenshtein distance</a>.</p>\n<p>After scratching my head for a while, I realized that this could be solved using - that's right - the dreadful <strong>dynamic programming</strong> (for some reason, while studying algorithms in a curricular unit early on in college, I ended up getting the idea dynamic programming was very hard, although that's not the case at all).</p>\n<p>By using some extra space to help the process, I ended up using a matrix to solve the problem. The matrix width represents <code>word1</code> and its height represents <code>word2</code>. Each matrix cell <code>(i, j)</code> value is the edit distance from the first <code>i</code> characters of <code>word1</code> to the first <code>j</code> characters of <code>word2</code>. Now I just needed to populate the matrix! After initializing all elements with 0 and filling the first row and first column with ascending values (which represent the edit distance of the word to an empty string), the edit distance is calculated using the following recurrence function:</p>\n<pre><code>if word1[i] == word2[j]:    // The char is the same, no distance\n    M[i, j] = M[i-1, j-1]\nelse:\n    M[i, j] = 1 + min(\n        M[i-1, j],          // Delete char at word1[i]\n        M[i, j-1],          // Insert char in word2[j]\n        M[i-1, j-1]         // Replace char in word1[i] by word2[j]\n    )\n</code></pre>\n<p>After iterating the full matrix, the edit distance is the value at <code>M[word1.size-1, word2.size-1]</code>! The full solution may be found <a href=\"https://github.com/xRuiAlves/leetcode/blob/master/Problems/72.%20Edit%20Distance/Solution.java\">here</a>.</p>\n<h2>Problem 210 - Course Schedule II [Medium]</h2>\n<pre><code>There are a total of n courses you have to take, labeled from 0 to n - 1.\nSome courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]\nGiven the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.\nThere may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.\n</code></pre>\n<p>I found this problem particularly fun to solve. The way I came up with the solution shows that, sometimes, you only fully understand the problem and get good ideas when you \"get your hands dirty\" and start coding. The first thing that caught my eye is that prerequisites could be modeled as connections in a directed graph and that the input may not lead to a valid course schedule - the courses could have requirement loops (direct or not).</p>\n<p>Finding if the input was valid or not was easily solved by applying a <a href=\"https://en.wikipedia.org/wiki/Depth-first_search\">DFS</a> starting at node 0 (the \"root\" of the graph) - If all nodes were visited without finding loops, the input could lead to a valid solution. That's when it came to my mind that applying the <code>DFS</code> would actually get me <strong>the solution itself</strong> if the input could lead to a valid course schedule!</p>\n<p>The order in which nodes are visited using a depth-first search consists of a <a href=\"https://en.wikipedia.org/wiki/Topological_sorting\">Topological Order</a> (or topological sorting) - which in this scenario represents a valid course schedule. The full solution may be found <a href=\"https://github.com/xRuiAlves/leetcode/blob/master/Problems/210.%20Course%20Schedule%20II/Solution.java\">here</a>.</p>\n<hr>\n<p>As of the of July 2020, I have solved <strong>73 easy</strong>, <strong>152 medium</strong> and <strong>13 hard</strong> problems. I'll keep solving problems from these monthly challenges for as long as I'm having fun with them - which will likely be for a long time.</p>\n<p>Cheers!</p>","frontmatter":{"date":"25 July, 2020","slug":"a-tour-around-competitive-programming","title":"A tour around Competitive Programming"}}},"pageContext":{"slug":"a-tour-around-competitive-programming"}},"staticQueryHashes":["3649515864","63159454"]}