{"id":1056,"date":"2020-04-24T21:53:50","date_gmt":"2020-04-25T04:53:50","guid":{"rendered":"http:\/\/209.126.2.187\/?p=1056"},"modified":"2020-05-02T10:35:17","modified_gmt":"2020-05-02T17:35:17","slug":"reduce-time-complexity-in-dp","status":"publish","type":"post","link":"https:\/\/nanzhou.cc\/index.php\/2020\/04\/24\/reduce-time-complexity-in-dp\/","title":{"rendered":"Reduce Time Complexity in DP"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this post, I will introduce some DP problems. It might not be difficult at the first glance. However, the optimal solution might have lower complexity if one carefully defines the state or uses some other techniques while transferring states. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">OJ<\/h2>\n\n\n\n\n<table id=\"tablepress-12\" class=\"tablepress tablepress-id-12\">\n<thead>\n<tr class=\"row-1\">\n\t<td class=\"column-1\"><\/td><th class=\"column-2\">Trick<\/th><th class=\"column-3\">Difficulty<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-striping row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\"><a href=\"https:\/\/leetcode.com\/problems\/longest-arithmetic-sequence\/\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 1027. Longest Arithmetic Sequence<\/a><\/td><td class=\"column-2\">DP in Linear Structures + Arithmetic<\/td><td class=\"column-3\">6 points<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\"><a href=\"https:\/\/leetcode.com\/problems\/arithmetic-slices-ii-subsequence\/\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 446. Arithmetic Slices II - Subsequence<\/a><\/td><td class=\"column-2\">DP in Linear Structures + Arithmetic<\/td><td class=\"column-3\">6 points<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\"><a href=\"https:\/\/leetcode.com\/problems\/make-array-strictly-increasing\/\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 1187. Make Array Strictly Increasing<\/a><\/td><td class=\"column-2\">DP in Linear Structures + Greedy<\/td><td class=\"column-3\">6 points<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\"><a href=\"https:\/\/leetcode.com\/problems\/build-array-where-you-can-find-the-maximum-exactly-k-comparisons\/\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 1420. Build Array Where You Can Find The Maximum Exactly K Comparisons<\/a><\/td><td class=\"column-2\">DP counting + Prefix Sum<\/td><td class=\"column-3\">6 points<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\"><a href=\"https:\/\/leetcode.com\/problems\/freedom-trail\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 514. Freedom Trail<\/a><\/td><td class=\"column-2\">DP in Linear Structures + Greedy<\/td><td class=\"column-3\">6 points<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\"><a href=\"m\/problems\/number-of-ways-to-wear-different-hats-to-each-other\/\" target=\"_blank\" rel=\"noopener noreferrer\"> LC 1434. Number of Ways to Wear Different Hats to Each Other<\/a><\/td><td class=\"column-2\">DP Knapsack + State Compression<\/td><td class=\"column-3\">6-7 points<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-12 from cache -->\n\n\n\n<h2 class=\"wp-block-heading\">Details<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"https:\/\/leetcode.com\/problems\/longest-arithmetic-sequence\/\" target=\"_blank\">LC 1027. Longest Arithmetic Sequence<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the following <code>O(n^2logn)<\/code> solution. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">alg[i][j] = \n alg[j][pos], let num=2*nums[j]-nums[i] in pos = lower_bound(num_to_pos[num], j + 1)<\/code><\/pre>\n\n\n\n<p>However, if we adopt the following state definition, the complexity reduces to <code>O(n^2)<\/code>. A tricky part is that the difference seems to be of <code>O(n^2)<\/code> complexity for an array. But here, the difference for each i will only be <code>O(n)<\/code> because of <code>j &gt; i<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ vector&lt;unordered_map>\nalg[i][diff]: sequence starting at i with different==diff\n\nlet diff=nums[j]-nums[i] in \n  alg[i][diff] = max {\n   diff in alg[j] ? alg[j][diff] + 1 : 2; for j in [i+1, n-1]}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"https:\/\/leetcode.com\/problems\/arithmetic-slices-ii-subsequence\/\" target=\"_blank\">LC 446. Arithmetic Slices II &#8211; Subsequence<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the following <code>O(n^3)<\/code> solution.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">alg[i][j] = \n   sum{ alg[j][k] == 0 ? 1 : alg[j][k] + 1 if nums[k] == 2 * nums[j] - nums[i] for k in [j+1, n-1] }\n\/\/ alg[j][k] + 1 because {nums[i] : sequence[j][k]} + {nums[i], nums[j], nums[k]}<\/code><\/pre>\n\n\n\n<p>However, if we use the same arithmetic difference technique, we can reduce the complexity to <code>O(n^2)<\/code>. An annoying thing here is that we only count subsequences of <code>length &gt;= 3<\/code>. We could use two tables to store subsequences of different lengths. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"> \/\/ sequence length > 2 \nalg[i][diff] = let diff = alg[j] - alg[i] in \n    sum{ {diff in alg[j] ? alg[j][diff] : 0} + {diff in alg2[j] ? alg2[j][diff] : 0} for j in [i + 1, n - 1]}\n    result += sum {alg[i].vals}\n    \n\/\/ sequence length == 2\nalg2[i][diff] = let diff = alg[j] - alg[i] in \n    alg2[i][diff]++ for j in [i + 1, n - 1]<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"https:\/\/leetcode.com\/problems\/make-array-strictly-increasing\/\" target=\"_blank\">LC 1187. Make Array Strictly Increasing<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the following <code>O(mnlog(n))<\/code> solution.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ prev is in arr1[i - 1]::arr2\nalg[i][prev] = \n  1. if arr1[i] > prev\n     1) alg[i + 1][arr1[i]] \n     \/\/ greadily choose the smallest larger one\n     2) 1 + alg[i + 1][j], j = upper_bound(arr2, prev); \n  2. if arr1[i] &lt;= prev\n     1) 1 + alg[i + 1][j], j = upper_bound(arr2, prev); <\/code><\/pre>\n\n\n\n<p>However, since <code>prev<\/code> is always in <code>arr1[i - 1]::arr2<\/code>, we could solve the problem in <code>O(mn)<\/code> by enumerating the index of <code>prev<\/code>. The advantage of using an index is that the next greater element is  <code>index+1<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ starting at i, previous is arr2[j], if j == n, it means it is arr1[i - 1]\nalg[i][j] = \n 1. if arr1[i] > arr2[j] &amp;&amp; j in [0, n - 2] \n     1) alg[i + 1][n]\n     2) 1 + alg[i + 1][j + 1]\n 2. if arr1[i] &lt;= arr2[j] &amp;&amp; j in [0, n - 2] \n     1) 1 + alg[i + 1][j + 1]\n 3. if arr1[i] > arr1[i - 1] &amp;&amp; j == n\n     1) alg[i + 1][n] \n     2) alg[i + 1][k], k = arr2.upper_bound(arr1[i]) - arr2.begin()\n 4. if arr[i] &lt;= arr1[i - 1] &amp;&amp; j == n\n     1) alg[i + 1][k], k = arr2.upper_bound(arr1[i]) - arr2.begin()\n 5. if arr1[i] > arr2[j] &amp;&amp; j == n - 1\n     1) alg[i + 1][n]\n 6. othercases will be -1<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"https:\/\/leetcode.com\/problems\/build-array-where-you-can-find-the-maximum-exactly-k-comparisons\/\" target=\"_blank\">LC 1420. Build Array Where You Can Find The Maximum Exactly K Comparisons<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the <code>O(nm^2K)<\/code> solution. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">alg[i][k][prev_max] = sum {\n  1. prev_max * alg[i + 1][k][prev_max]\n  2. alg[i + 1][k + 1][j] for j in [prev_max + 1, m]\n\n\/\/ alg[n][k][j] = 1 for j in [1, m]<\/code><\/pre>\n\n\n\n<p>However, as we could see, the nested loop which enumerates the next <code>prev_max<\/code> could be achieved by the prefix sum technique. To make implementation easier, I use another table to store prefix sum, which reduces the complexity to <code>O(nmK)<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">alg[i][k][prev_max] = sum {\n  1. prev_max * alg[i + 1][k][prev_max]\n  2. alg2[i + 1][k + 1][prev_max+1] == sum {alg[i + 1][k + 1][j] for j in [prev_max + 1, m]}\n\n\/\/ alg[n][k][j] = 1 for j in [1, m]\n    \n\/\/ alg2[i][k][m] = alg[i][k][m]\nalg2[i][k][j] = alg[i][k][j] + alg2[i][k][j + 1]<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"https:\/\/leetcode.com\/problems\/freedom-trail\/submissions\/\" target=\"_blank\">LC 514. Freedom Trail<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the <code>O(nm^2)<\/code> solution.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ ring m, key n\n\/\/ ch_to_index is for ring\nalg[j][i]\n = cost(i, k) + alg[j + 1][k] for k in ch_to_index[key[j]]]<\/code><\/pre>\n\n\n\n<p>Actually, given any <code>key[j]<\/code> and the current position <code>i<\/code> at ring, we could greedily choose two same characters in ring: the nearest right and the nearest left. We could preprocess this in <code>O(26mlogm)<\/code> time. Then the time complexity reduces to <code>O(nm)<\/code>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">alg[j][i]\n = min(cost(i, left) + alg[j + 1][left], cost(i, right) + alg[j + 1][right])<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><a rel=\"noreferrer noopener\" href=\"http:\/\/161.97.122.139\/wp-admin\/m\/problems\/number-of-ways-to-wear-different-hats-to-each-other\/\" target=\"_blank\">LC 1434. Number of Ways to Wear Different Hats to Each Other<\/a><\/h4>\n\n\n\n<p>It is not difficult to get the <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nanzhou.cc\/wp-content\/ql-cache\/quicklatex.com-1f82a2a334d84881a2b6beb5dd550672_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#79;&#40;&#110;&#95;&#123;&#112;&#101;&#111;&#112;&#108;&#101;&#125;&#92;&#116;&#105;&#109;&#101;&#115;&#32;&#110;&#95;&#123;&#104;&#97;&#116;&#125;&#92;&#116;&#105;&#109;&#101;&#115;&#50;&#94;&#123;&#110;&#95;&#123;&#104;&#97;&#116;&#125;&#125;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"20\" width=\"188\" style=\"vertical-align: -6px;\"\/> solution. Given <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nanzhou.cc\/wp-content\/ql-cache\/quicklatex.com-bf9aeeca31368474207f59a6034167a3_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#110;&#95;&#123;&#112;&#101;&#111;&#112;&#108;&#101;&#125;&#61;&#49;&#48;\" title=\"Rendered by QuickLaTeX.com\" height=\"18\" width=\"91\" style=\"vertical-align: -6px;\"\/> and  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nanzhou.cc\/wp-content\/ql-cache\/quicklatex.com-2326c36321da56e33fc2248fbb1ca227_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#110;&#95;&#123;&#104;&#97;&#116;&#125;&#61;&#52;&#48;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"73\" style=\"vertical-align: -3px;\"\/>, this solution will get TLE. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ n_people; n_hat \n\/\/ i here is the index of people\nalg[i][visited] = \n  alg[i + 1][visited | num_to_hat_index[hat]] if num_to_hat_index[hat] not in visited &amp;&amp; hat in hats[i] for hat in [0, 40]<\/code><\/pre>\n\n\n\n<p>We could think on the other hand: let the hat choose people. Then the complexity becomes <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nanzhou.cc\/wp-content\/ql-cache\/quicklatex.com-43c1355aaf4f667dad38339af4a3165a_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#79;&#40;&#110;&#95;&#123;&#112;&#101;&#111;&#112;&#108;&#101;&#125;&#92;&#116;&#105;&#109;&#101;&#115;&#32;&#110;&#95;&#123;&#104;&#97;&#116;&#125;&#92;&#116;&#105;&#109;&#101;&#115;&#50;&#94;&#123;&#110;&#95;&#123;&#112;&#101;&#111;&#112;&#108;&#101;&#125;&#125;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"20\" width=\"203\" style=\"vertical-align: -6px;\"\/><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">\/\/ i here is the index of hat\nalg[i][visited]\n  1. alg[i + 1][visited | (1 &lt;&lt; j)] if (1 &lt;&lt; j) &amp; visited == 0 &amp;&amp; distinct_hat[i] in people_to_hat_set[j] for j in [0, n - 1] \n  2. alg[i + 1][visited] \/\/ ith hat could not be chosen\n\/\/ alg[n_hat][(1 &lt;&lt; n_people) - 1] = 1<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Summary In this post, I will introduce some DP problems. It might not be difficult at the first glance. However, the optimal solution might have lower complexity if one carefully defines the state or uses some other techniques while transferring states. OJ Details LC 1027. Longest Arithmetic Sequence It is not difficult to get the&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48,55],"tags":[],"class_list":["post-1056","post","type-post","status-publish","format-standard","hentry","category-alg","category-dp-in-linear-structures"],"_links":{"self":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1056","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/comments?post=1056"}],"version-history":[{"count":10,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1056\/revisions"}],"predecessor-version":[{"id":1141,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/posts\/1056\/revisions\/1141"}],"wp:attachment":[{"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/media?parent=1056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/categories?post=1056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nanzhou.cc\/index.php\/wp-json\/wp\/v2\/tags?post=1056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}