fix(posts): fetch daily from files

This commit is contained in:
Barrett Ruth 2024-11-29 22:13:16 -06:00
parent 3d2ee97afe
commit 2bcc3f4675
9 changed files with 691 additions and 0 deletions

View file

@ -0,0 +1,570 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles/common.css" />
<link rel="stylesheet" href="/styles/post.css" />
<link rel="icon" type="image/webp" href="/public/logo.webp" />
<link href="/public/prism/prism.css" rel="stylesheet" />
<link href="/public/prism/prism-theme.css" rel="stylesheet" />
<script defer src="/public/prism/prism.js"></script>
<script
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
async
></script>
<title>Barrett Ruth</title>
</head>
<body class="graph-background">
<header>
<a
href="/"
style="text-decoration: none; color: inherit"
onclick="goHome(event)"
>
<div class="terminal-container">
<span class="terminal-prompt">barrett@ruth:~$ /algorithms</span>
<span class="terminal-cursor"></span>
</div>
</a>
</header>
<main class="main">
<div class="post-container">
<header class="post-header">
<h1 class="post-title">Leetcode Daily</h1>
</header>
<article class="post-article">
<h2>
<a
target="blank"
href="https://leetcode.com/problems/count-the-number-of-fair-pairs/"
>count the number of fair pairs</a
>
&mdash; 9/13/24
</h2>
<div class="problem-content">
<h3>problem statement</h3>
<p>
Given an array <code>nums</code> of integers and upper/lower
integer bounds <code>upper</code>/<code>lower</code> respectively,
return the number of unique valid index pairs such that: \[i\neq
j,lower\leq nums[i]+nums[j]\leq upper\]
</p>
<h3>understanding the problem</h3>
<p>
This is another sleeper daily in which a bit of thinking in the
beginning pays dividends. Intuitively, I think it makes sense to
reduce the &ldquo;dimensionality&rdquo; of the problem. Choosing
both <code>i</code> and <code>j</code> concurrently seems tricky,
so let&apos;s assume we&apos;ve found a valid <code>i</code>. What
must be true? Well: \[i\neq j,lower-nums[i]\leq nums[j]\leq
upper-nums[i]\]
</p>
<p>
It doesn&apos;t seem like we&apos;ve made much progress. If nums
is a sequence of random integers,
<i
>there&apos;s truly no way to find all <code>j</code> satisfying
this condition efficiently</i
>.
</p>
<p>
The following question naturally arises: can we modify our input
to find such <code>j</code> efficiently? Recall our goal: find the
smallest/largest j to fit within our altered bounds&mdash;in other
words, find the smallest \(x\) less/greater than or equal to a
number. If binary search bells aren&apos;t clanging in your head
right now, I&apos;m not sure what to say besides keep practicing.
</p>
<p>
So, it would be nice to sort <code>nums</code> to find such
<code>j</code> relatively quickly. However:
<i>are we actually allowed to do this?</i> This is the core
question I think everyone skips over. Maybe it is trivial but it
is important to emphasize:
</p>
<ul style="list-style: none">
<li>
<i>Yes, we are allowed to sort the input</i>. Re-frame the
problem: what we are actually doing is choosing distinct
<code>i</code>, <code>j</code> to satisfy some condition. The
order of <code>nums</code> does not matter&mdash;rather, its
contents do. Any input to this algorithm with
<code>nums</code> with the same contents will yield the same
result. If we were to modify <code>nums</code> instead of
rearrange it, this would be invalid because we could be
introducing/taking away valid index combinations.
</li>
</ul>
<p>
Let&apos;s consider our solution a bit more before implementing
it:
</p>
<ul>
<li>
Is the approach feasible? We&apos;re sorting
<code>nums</code> then binary searching over it considering all
<code>i</code>, which will take around \(O(nlg(n))\) time.
<code>len(nums)</code>\(\leq10^5\), so this is fine.
</li>
<li>
How do we avoid double-counting? The logic so far makes no
effort. If we consider making all pairs with indices
<i>less than</i> <code>i</code> for all
<code>i</code> left-to-right, we&apos;ll be considering all
valid pairs with no overlap. This is a common pattern&mdash;take
a moment to justify it to yourself.
</li>
<li>
<i>Exactly</i> how many elements do we count? Okay, we&apos;re
considering some rightmost index <code>i</code> and we&apos;ve
found upper and lower index bounds <code>j</code> and
<code>k</code> respectively. We can pair
<code>nums[j]</code> with all elements up to an including
<code>nums[k]</code> (besides <code>nums[j]</code>). There are
exactly \(k-j\) of these. If the indexing confuses you, draw it
out and prove it to yourself.
</li>
<li>
How do we get our final answer? Accumulate all
<code>k-j</code> for all <code>i</code>.
</li>
</ul>
<h3>carrying out the plan</h3>
<p>
The following approach implements our logic quite elegantly and
directly. The third and fourth arguments to the
<code>bisect</code> calls specify <code>lo</code> (inclusive) and
<code>hi</code> (exclusive) bounds for our search space, mirroring
the criteria that we search across all indices \(\lt i\).
</p>
<div class="code" data-file="cfps-naive.py"></div>
<h3>optimizing the approach</h3>
<p>
If we interpret the criteria this way, the above approach is
relatively efficient. To improve this approach, we&apos;ll need to
reinterpret the constraints. Forget about the indexing and
consider the constraint in aggregate. We want to find all \(i,j\)
with \(x=nums[i]+nums[j]\) such that \(i\neq j,lower\leq x\leq
upper\).
</p>
<p>
We <i>still</i> need to reduce the &ldquo;dimensionality&rdquo; of
the problem&mdash;there are just too many moving parts to consider
at once. This seems challening. Let&apos;s simplify the problem to
identify helpful ideas: pretend <code>lower</code> does not exist
(and, of course, that <code>nums</code> is sorted).
</p>
<p>
We&apos;re looking for all index pairs with sum \(\leq upper\).
And behold: (almost) two sum in the wild. This can be accomplished
with a two-pointers approach&mdash;this post is getting quite long
so we&apos;ll skip over why this is the case&mdash;but the main
win here is that we can solve this simplified version of our
problem in \(O(n)\).
</p>
<p>
Are we any closer to actually solving the problem? Now, we have
the count of index pairs \(\leq upper\). Is this our answer?
No&mdash;some may be too small, namely, with sum \(\lt lower\).
Let&apos;s exclude those by running our two-pointer approach with
and upper bound of \(lower-1\) (we want to include \(lower\)).
Now, our count reflects the total number of index pairs with a sum
in our interval bound.
</p>
<p>
Note that this really is just running a prefix sum/using the
&ldquo;inclusion-exclusion&rdquo; principle/however you want to
phrase it.
</p>
<div class="code" data-file="cfps-twoptr.py"></div>
<h3>some more considerations</h3>
<p>
The second approach is <i>asymptotically</i> equivalent. However,
it&apos;s still worth considering for two reasons:
</p>
<ol>
<li>
If an interviewer says &ldquo;assume <code>nums</code> is
sorted&rdquo; or &ldquo;how can we do
better?&rdquo;&mdash;you&apos;re cooked.
</li>
<li>
(Much) more importantly, it&apos;s extremely valuable to be able
to <i>reconceptualize</i> a problem and look at it from
different angles. Not being locked in on a solution shows
perseverance, curiosity, and strong problem-solving abilities.
</li>
</ol>
<h3>asymptotic complexity</h3>
<p>
<u>Time Complexity</u>: \(O(nlg(n))\) for both&mdash;\(O(n)\) if
<code>nums</code> is sorted with respect to the second approach.
</p>
<p><u>Space Complexity</u>: \(\Theta(1)\) for both.</p>
</div>
<h2>
<a
target="blank"
href="https://leetcode.com/problems/most-beautiful-item-for-each-query/description/"
>most beautiful item for each query</a
>
&mdash; 9/12/24
</h2>
<div class="problem-content">
<h3>problem statement</h3>
<p>
Given an array <code>items</code> of \((price, beauty)\) tuples,
answer each integer query of \(queries\). The answer to some
<code>query[i]</code> is the maximum beauty of an item with
\(price\leq\)<code>items[i][0]</code>.
</p>
<h3>understanding the problem</h3>
<p>
Focus on one aspect of the problem at a time. To answer a query,
we need to have considered:
</p>
<ol>
<li>Items with a non-greater price</li>
<li>The beauty of all such items</li>
</ol>
<p>
Given some query, how can we <i>efficiently</i> identify the
&ldquo;last&rdquo; item with an acceptable price? Leverage the
most common pre-processing algorithm: sorting. Subsequently, we
can binary search <code>items</code> (keyed by price, of course)
to identify all considerable items in \(O(lg(n))\).
</p>
<p>
Great. Now we need to find the item with the largest beauty.
Na&iuml;vely considering all the element is a
<i>correct</i> approach&mdash;but is it correct? Considering our
binary search \(O(lg(n))\) and beauty search \(O(n)\) across
\(\Theta(n)\) queries with
<code>len(items)<=len(queries)</code>\(\leq10^5\), an
\(O(n^2lg(n))\) approach is certainly unacceptable.
</p>
<p>
Consider alternative approaches to responding to our queries. It
is clear that answering them in-order yields no benefit (i.e. we
have to consider each item all over again, per query)&mdash;could
we answer them in another order to save computations?
</p>
<p>
Visualizing our items from left-to-right, we&apos;s interested in
both increasing beauty and prices. If we can scan our items left
to right, we can certainly &ldquo;accumulate&rdquo; a running
maximal beauty. We can leverage sorting once again to answer our
queries left-to-right, then re-order them appropriately before
returning a final answer. Sorting both <code>queries</code> and
<code>items</code> with a linear scan will take \(O(nlg(n))\)
time, meeting the constraints.
</p>
<h3>carrying out the plan</h3>
<p>
A few specifics need to be understood before coding up the
approach:
</p>
<ul>
<li>
Re-ordering the queries: couple <code>query[i]</code> with
<code>i</code>, then sort. When responding to queries in sorted
order, we know where to place them in an output
container&mdash;index <code>i</code>.
</li>
<li>
The linear scan: accumulate a running maximal beauty, starting
at index <code>0</code>. For some query <code>query</code>, we
want to consider all items with price less than or equal to
<code>query</code>. Therefore, loop until this condition is
<i>violated</i>&mdash; the previous index will represent the
last considered item.
</li>
<li>
Edge cases: it&apos;s perfectly possible the last considered
item is invalid (consider a query cheaper than the cheapest
item). Return <code>0</code> as specified by the problem
constraints.
</li>
</ul>
<div class="code" data-file="beauty.cpp"></div>
<h3>asymptotic complexity</h3>
<p>
Let <code>n=len(items)</code> and <code>m=len(queries)</code>.
There may be more items than queries, or vice versa. Note that a
&ldquo;looser&rdquo; upper bound can be found by analyzing the
runtime in terms of \(max\{n,m\}\).
</p>
<p>
<u>Time Complexity</u>: \(O(nlg(n)+mlg(m)+m)\in
O(nlg(n)+mlg(m))\). An argument can be made that because
<code>queries[i],items[i][{0,1}]</code>\(\leq10^9\), radix sort
can be leveraged to achieve a time complexity of \(O(d \cdot (n +
k + m + k))\in O(9\cdot (n + m))\in O(n+m)\).
</p>
<p>
<u>Space Complexity</u>: \(\Theta(1)\), considering that \(O(m)\)
space must be allocated. If <code>queries</code>/<code
>items</code
>
cannot be modified in-place, increase the space complexity by
\(m\)/\(n\) respectively.
</p>
</div>
<h2>
<a
target="blank"
href="https://leetcode.com/problems/shortest-subarray-with-or-at-least-k-ii/description/"
>shortest subarray with or at least k ii</a
>
&mdash; 9/11/24
</h2>
<div class="problem-content">
<h3>problem statement</h3>
<p>
Given an array of non-negative integers \(num\) and some \(k\),
find the length of the shortest non-empty subarray of nums such
that its element-wise bitwise OR is greater than or equal to
\(k\)&mdash;return -1 if no such array exists.
</p>
<h3>developing an approach</h3>
<p>Another convoluted, uninspired bitwise-oriented daily.</p>
<p>
Anways, we&apos;re looking for a subarray that satisfies a
condition. Considering all subarrays with
<code>len(nums)</code>\(\leq2\times10^5\) is impractical according
to the common rule of \(\approx10^8\) computations per second on
modern CPUs.
</p>
<p>
Say we&apos;s building some array <code>xs</code>. Adding another
element <code>x</code> to this sequence can only increase or
element-wise bitwise OR. Of course, it makes sense to do this.
However, consider <code>xs</code> after&mdash;it is certainly
possible that including <code>x</code> finally got us to at least
<code>k</code>. However, not all of the elements in the array are
useful now; we should remove some.
</p>
<p>
Which do we remove? Certainly not any from the
middle&mdash;we&apos;d no longer be considering a subarray. We can
only remove from the beginning.
</p>
<p>
Now, how many times do we remove? While the element-wise bitwise
OR of <code>xs</code> is \(\geq k\), we can na&iuml;vely remove
from the start of <code>xs</code> to find the smallest subarray.
</p>
<p>
Lastly, what&apos; the state of <code>xs</code> after these
removals? Now, we (may) have an answer and the element-wise
bitwise OR of <code>xs</code> is guaranteed to be \(\lt k\).
Inductively, expand the array to search for a better answer.
</p>
<p>
This approach is generally called a variable-sized &ldquo;sliding
window&rdquo;. Every element of
<code>nums</code> is only added (considered in the element-wise
bitwise OR) or removed (discard) one time, yielding an
asymptotically linear time complexity. In other words, this is a
realistic approach for our constraints.
</p>
<h3>carrying out the plan</h3>
<p>Plugging in our algorithm to my sliding window framework:</p>
<div class="code" data-file="msl-naive.py"></div>
<p>Done, right? No. TLE.</p>
<p>
If you thought this solution would work, you move too fast.
Consider <i>every</i> aspect of an algorithm before implementing
it. In this case, we (I) overlooked one core question:
</p>
<ol style="list-style: none">
<li><i>How do we maintain our element-wise bitwise OR</i>?</li>
</ol>
<p>
Calculating it by directly maintaining a window of length \(n\)
takes \(n\) time&mdash;with a maximum window size of \(n\), this
solution is \(O(n^2)\).
</p>
<p>
Let&apos;s try again. Adding an element is simple&mdash;OR it to
some cumulative value. Removing an element, not so much.
Considering some \(x\) to remove, we only unset one of its bits
from our aggregated OR if it&apos;s the &ldquo;last&rdquo; one of
these bits set across all numbers contributing to our aggregated
value.
</p>
<p>
Thus, to maintain our aggregate OR, we want to map bit
&ldquo;indices&rdquo; to counts. A hashmap (dictionary) or static
array will do just find. Adding/removing some \(x\) will
increment/decrement each the counter&apos;s bit count at its
respective position. I like to be uselessly specific
sometimes&mdash;choosing the latter approach, how big should our
array be? As many bits as represented by the largest of
\(nums\)&mdash;(or \(k\) itself): \[\lfloor \lg({max\{nums,k
\})}\rfloor+1\]
</p>
<p>Note that:</p>
<ol>
<li>
Below we use the
<a
target="_blank"
href="https://artofproblemsolving.com/wiki/index.php/Change_of_base_formula"
>change of base formula for logarithms</a
>
because \(log_2(x)\) is not available in python.
</li>
<li>
It&apos;s certainly possible that \(max\{nums, k\}=0\). To avoid
the invalid calculation \(log(0)\), take the larger of \(1\) and
this calculation. The number of digits will then (correctly) be
\(1\) in this special case.
</li>
</ol>
<div class="code" data-file="msl-bitwise.py"></div>
<h3>asymptotic complexity</h3>
<p>
Note that the size of the frequency map is bounded by
\(lg_{2}({10^9})\approx30\).
</p>
<p>
<u>Space Complexity</u>: Thus, the window uses \(O(1)\) space.
</p>
<p>
<u>Time Complexity</u>: \(\Theta(\)<code>len(nums)</code>\()\)
&mdash;every element of <code>nums</code> is considered at least
once and takes \(O(1)\) work each to find the element-wise bitwise
OR.
</p>
</div>
<h2>
<a
target="blank"
href="https://leetcode.com/problems/minimum-array-end/"
>minimum array end</a
>
&mdash; 9/10/24
</h2>
<div class="problem-content">
<h3>problem statement</h3>
<p>
Given some \(x\) and \(n\), construct a strictly increasing array
(say
<code>nums</code>
) of length \(n\) such that
<code>nums[0] & nums[1] ... & nums[n - 1] == x</code>
, where
<code>&</code>
denotes the bitwise AND operator.
</p>
<p>
Finally, return the minimum possible value of
<code>nums[n - 1]</code>.
</p>
<h3>understanding the problem</h3>
<p>
The main difficulty in this problem lies in understanding what is
being asked (intentionally or not, the phrasing is terrible). Some
initial notes:
</p>
<ul>
<li>The final array need not be constructed</li>
<li>
If the element-wise bitwise AND of an array equals
<code>x</code> if and only if each element has
<code>x</code>&apos;s bits set&mdash;and no other bit it set by
all elements
</li>
<li>
It makes sense to set <code>nums[0] == x</code> to ensure
<code>nums[n - 1]</code> is minimal
</li>
</ul>
<h3>developing an approach</h3>
<p>
An inductive approach is helpful. Consider the natural question:
&ldquo;If I had correctly generated <code>nums[:i]</code>&rdquo;,
how could I find <code>nums[i]</code>? In other words,
<i
>how can I find the next smallest number such that
<code>nums</code>
&apos;s element-wise bitwise AND is still \(x\)?</i
>
</p>
<p>
Hmm... this is tricky. Let&apos;s think of a similar problem to
glean some insight: &ldquo;Given some \(x\), how can I find the
next smallest number?&rdquo;. The answer is, of course, add one
(bear with me here).
</p>
<p>
We also know that all of <code>nums[i]</code> must have at least
\(x\)&apos;s bits set. Therefore, we need to alter the unset bits
of <code>nums[i]</code>.
</p>
<p>
The key insight of this problem is combining these two ideas to
answer our question:
<i
>Just &ldquo;add one&rdquo; to <code>nums[i - 1]</code>&apos;s
unset bits</i
>. Repeat this to find <code>nums[n - 1]</code>.
</p>
<p>
One last piece is missing&mdash;how do we know the element-wise
bitwise AND is <i>exactly</i> \(x\)? Because
<code>nums[i > 0]</code> only sets \(x\)&apos;s unset bits, every
number in <code>nums</code> will have at least \(x\)&apos;s bits
set. Further, no other bits will be set because \(x\) has them
unset.
</p>
<h3>carrying out the plan</h3>
<p>Let&apos;s flesh out the remaining parts of the algorithm:</p>
<ul>
<li>
<code>len(nums) == n</code> and we initialize
<code>nums[0] == x</code>. So, we need to &ldquo;add one&rdquo;
<code>n - 1</code> times
</li>
<li>
How do we carry out the additions? We could iterate \(n - 1\)
times and simulate them. However, we already know how we want to
alter the unset bits of <code>nums[0]</code> inductively&mdash;
(add one) <i>and</i> how many times we want to do this (\(n -
1\)). Because we&apos;re adding one \(n-1\) times to
\(x\)&apos;s unset bits (right to left, of course), we simply
set its unset bits to those of \(n - 1\).
</li>
</ul>
<p>
The implementation is relatively straightfoward. Traverse \(x\)
from least-to-most significant bit, setting its \(i\)th unset bit
to \(n - 1\)&apos;s \(i\)th bit. Use a bitwise mask
<code>mask</code> to traverse \(x\).
</p>
<div class="code" data-file="minend.cpp"></div>
<h3>asymptotic complexity</h3>
<p>
<u>Space Complexity</u>: \(\Theta(1)\)&mdash;a constant amount of
numeric variables are allocated regardless of \(n\) and \(x\).
</p>
<p>
<u>Time Complexity</u>: in the worst case, may need to traverse
the entirety of \(x\) to distribute every bit of \(n - 1\) to
\(x\). This occurs if and only if \(x\) is all ones (\(\exists
k\gt 0 : 2^k-1=x\))). \(x\) and \(n\) have \(lg(x)\) and \(lg(n)\)
bits respectively, so the solution is \(O(lg(x) + lg(n))\in
O(log(xn))\). \(1\leq x,n\leq 1e8\), so this runtime is bounded by
\(O(log(1e8^2))\in O(1)\).
</p>
</div>
</article>
</div>
</main>
<script src="/scripts/common.js"></script>
<script src="/scripts/post.js"></script>
</body>
</html>

View file

@ -0,0 +1,28 @@
#include <vector>
#include <algorithm>
std::vector<int> maximumBeauty(std::vector<std::vector<int>>& items, std::vector<int>& queries) {
std::sort(items.begin(), items.end());
std::vector<std::pair<int, int>> sorted_queries;
sorted_queries.reserve(queries.size());
// couple queries with their indices
for (size_t i = 0; i < queries.size(); ++i) {
sorted_queries.emplace_back(queries[i], i);
}
std::sort(sorted_queries.begin(), sorted_queries.end());
int beauty = items[0][1];
size_t i = 0;
std::vector<int> ans(queries.size());
for (const auto [query, index] : sorted_queries) {
while (i < items.size() && items[i][0] <= query) {
beauty = std::max(beauty, items[i][1]);
++i;
}
// invariant: items[i - 1] is the rightmost considerable item
ans[index] = i > 0 && items[i - 1][0] <= query ? beauty : 0;
}
return std::move(ans);
}

View file

@ -0,0 +1,11 @@
def countFairPairs(self, nums, lower, upper):
nums.sort()
ans = 0
for i, num in enumerate(nums):
k = bisect_left(nums, lower - num, 0, i)
j = bisect_right(nums, upper - num, 0, i)
ans += k - j
return ans

View file

@ -0,0 +1,16 @@
def countFairPairs(self, nums, lower, upper):
nums.sort()
ans = 0
def pairs_leq(x: int) -> int:
pairs = 0
l, r = 0, len(nums) - 1
while l < r:
if nums[l] + nums[r] <= x:
pairs += r - l
l += 1
else:
r -= 1
return pairs
return pairs_leq(upper) - pairs_leq(lower - 1)

View file

@ -0,0 +1,16 @@
long long minEnd(int n, long long x) {
int bits_to_distribute = n - 1;
long long mask = 1;
while (bits_to_distribute > 0) {
if ((x & mask) == 0) {
// if the bit should be set, set it-otherwise, leave it alone
if ((bits_to_distribute & 1) == 1)
x |= mask;
bits_to_distribute >>= 1;
}
mask <<= 1;
}
return x;
}

View file

@ -0,0 +1,30 @@
def minimumSubarrayLength(self, nums, k):
ans = sys.maxsize
largest = max(*nums, k)
num_digits = floor((log(max(largest, 1))) / log(2)) + 1
counts = [0] * num_digits
l = 0
def update(x, delta):
for i in range(len(counts)):
if x & 1:
counts[i] += delta
x >>= 1
def bitwise_or():
return reduce(
operator.or_,
(1 << i if count else 0 for i, count in enumerate(counts)),
0
)
for r, num in enumerate(nums):
update(num, 1)
while l <= r and bitwise_or() >= k:
ans = min(ans, r - l + 1)
update(nums[l], -1)
l += 1
return -1 if ans == sys.maxsize else ans

View file

@ -0,0 +1,18 @@
def minimumSubarrayLength(self, nums, k):
# provide a sentinel for "no window found"
ans = sys.maxsize
window = deque()
l = 0
# expand the window by default
for r in range(len(nums)):
# consider `nums[r]`
window.append(nums[r])
# shrink window while valid
while l <= r and reduce(operator.or_, window) >= k:
ans = min(ans, r - l + 1)
window.popleft()
l += 1
# normalize to -1 as requested
return -1 if ans == sys.maxsize else ans

View file

@ -19,6 +19,7 @@ const postMapping = new Map([
[
"Algorithms",
[
{ name: "leetcode daily", link: "leetcode-daily" },
{ name: "extrema circular buffer", link: "extrema-circular-buffer" },
],
],