Binary Search, Two Sum and Strings
Binary Search, Two Sum and Strings
Binary Search
Assume you are reading a book, and you want to read Page 747. How can you reach the page?
One way is to go through all the pages one by one till you reach 747. But this will take up a lot of
our time.
What we generally do is, open a random page, and check the page number. If the current page
number is less than 747, follow the same process on the right side pages. Similarly, if the current
page number is greater than 747, check the left side.
Now, in our case, we can be a bit biased while selecting the page to open first during searching.
Because we know the page numbers follow a particular order like 1, 2, 3, … and we also know the
last page number. So let’s say the total number of pages is 800. So we know 747 is towards the
end. So, we will open up a page towards the end of the book in the first iteration.
However, in real life, we do not always have this information. So picking a pivot is crucial for the
search to be faster. It has been found that, the search is fastest in worst case when we choose
the middle element as the pivot. This leads us to the concept of binary search.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 1/15
7/9/24, 12:08 AM DailyCode
2. Now we know 7 > 4. And as the array is increasing, it is definitely on the RHS. So we can just
discard the left half and continue our search on the right half. So, the new search space we
are considering is [5, 6, 7, 8]
3. Repeat the same process. The middle element is 6. As 7 > 6, we know it lies on the RHS. So
reject the left half. New search space becomes [7, 8]
4. The middle element is 7. As it is the same as the requested element, so we return it.
Complexity Analysis
In binary search, as discussed, half of the search space is rejected every iteration. So in the worst
case, we will be continuing till we are left with at least one element.
So initially we start with N elements.
After the first iteration, we have N/2 elements.
N -> N/2 -> N/4 -> N/8 -> N/16 -> ..... -> 1 Copy
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 2/15
7/9/24, 12:08 AM DailyCode
N -> N/2 -> N/(2^2) -> N/(2^3) -> ..... -> N/(2^k) Copy
So k = log2(N)
The overall complexity of the binary search algorithm is, log2(N) where N is the search space.
Code
Question: 2 sum
You are given an array of N elements, and also a number k. Find if there are 2 elements, whose
sum is equal to k.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 3/15
7/9/24, 12:08 AM DailyCode
Solution
We can fix every element and try searching for the other element, such that their sum is k. If
found, return true, else false.
For that, we can sort the array first. Now go through all the elements one by one. For every
element i the question becomes, is there another element which is equal to k - arr[i]
return false;
}
return false;
}
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 4/15
7/9/24, 12:08 AM DailyCode
Link: https://leetcode.com/problems/count-pairs-whose-sum-is-less-than-target/description/
Solution
As discussed earlier, whenever we have problems related to pairs, we can fix one element (as
the right element), and search on the LHS of that element.
For each fixed element j we want to find the number of elements on the LHS that are less
than target - arr[j] .
Let’s assume the array is sorted. How can we find the number of elements <k in the array?
We can find the first index ≥ k (let’s say the index is idx), then the number of elements <k is
idx (0 - idx-1).
So we will sort, our main array and run the algorithm. That is iterate over all elements and fix
them as j . For each element identify how many elements are there on the L.H.S i.e (0, j-1)
that are < (target - arr[j]) using Binary Search.
Solution extended
So, in the above case, it is similar to finding lower-bound of target - arr[j] .
return hi;
}
int countPairs(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int ans = 0;
for(int i=1; i<nums.size(); i++) {
ans += compute(nums, target - nums[i], 0, i-1);
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 5/15
7/9/24, 12:08 AM DailyCode
return ans;
}
};
Firstly, what do I mean by lower-bound . It is a function that is available with the algorithm
library. It returns the first index ≥ x. In these cases, you are not just searching if an element is
present or not, but rather the first index that satisfies a monotonic property.
Let’s understand with an example. In the following array, find the first index that is greater than
or equal to 5
I see these types of questions as 0-1 questions, where you can generate a 0-1 array based on the
property. The property is ≥ 5.
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] Copy
💡 There are multiple ways to solve this. Below I have outlined the approach that I use and
find more intuitive.
In these arrays, we need to find the first occurrence of 1. Now traditional binary search doesn’t
work here as it would return true whenever it sees a one. We specifically want the first
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 6/15
7/9/24, 12:08 AM DailyCode
occurrence of 1. So we will tweak our binary search implementation a bit to tackle this.
If we think about the statements above, where should we end the loop?
In a state like this [0, 1] , where lo points to the last 0 and hi points to the first 1.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 7/15
7/9/24, 12:08 AM DailyCode
Link: https://leetcode.com/problems/find-peak-element/
Solution
Is there a property that is getting satisfied by each array element and is monotonic?
In the first half we have elements arr[i] > arr[i-1]
In the second half, it is arr[i] < arr[i-1]
So we get [null, 0, 0, 0, 1, 1]
Code
return lo;
}
};
We discussed earlier, that if we have some monotonic array, where each element satisfies a
monotonic property, we can binary search on the monotonic property using 0-1 array.
But, this holds true even for a non-monotonic array. In general, if we have some property that is
monotonic, we can binary search for it.
We saw that with the peak element example, the array was not monotonic. But we identified a
property arr[i] > arr[i-1] that is monotonic, and based on that did a binary search. However,
often the property that makes it monotonic is not so simple.
Here the weights in not a monotonic array (i.e. neither increasing nor decreasing).
Now for X, if it takes more than D days, obviously if the capacity is < X, then also it takes more
than D days. (Think like this, if the ship can carry max capacity of 10 kgs and with this it takes 5
days, then if it has max capacity of 5 kgs, obviously it will take ≥ 5 days)
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 9/15
7/9/24, 12:08 AM DailyCode
Similarly, if the ship takes ≤D days with X capacity, it will obviously take ≤D days when its
capacity is > X. (Example, if ship can carry 10kgs and it takes 5 days, obviously it will take ≤ 5
days if its capacity was 15 kgs)
So the property maximum capacity of the ship is itself monotonic with the number of days taken.
As max capacity increases, the days taken decrease. As capacity decreases, days taken increase.
Let’s say G(x) returns the number of days taken when max capacity is x.
F(x) is whether the ship can transport all the goods within D days if its max capacity is x.
For lower values of x, G(x) is more and thus F(x) is false. As x increases, a point comes when G(x)
≤ D, and F(x) becomes true. And from there, it always stays true as x increases.
Now, what do we want. Minimum possible value of the maximum capacity. So we want the first
x, where F(x) becomes true. 🙂
That’s it!
return hi;
Now what is pending to identify? LOW_VAL, HIGH_VAL and how is G(x) calculated
LOW_VAL = Lowest possible value of maximum capacity. That is the maximum capacity cannot
be lower than this. So in the worst case, we need to carry all the packages right? And one
package / day. So the ship should be at least able to carry the maximum weighted package.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 10/15
7/9/24, 12:08 AM DailyCode
MAX_VAL = Maximum possible capacity that is needed for our problem. In best case, the ship
transfers all the goods in one day itself. So at max, it needs a total capacity of total sum of all the
weights.
Finally G(x). What is it? Given max carrying capacity of the ship is x, how many days would it take.
We can just run a linear search for this because it has to transfer all the weights in order.
return days;
}
Now you might be wondering, wouldn’t it take a lot of time, or not meet the time constraints?
Let’s analyze the complexity. We are running a binary search. For each binary search iteration, we
are calling F(mid) which in turn calls G(mid) . So each iteration takes O(N) time. But how many
iterations in binary search? It’s log2(N) .
💡 Generally, if you come across problems like minimizing the maximum value of some
property or maximizing the minimum value of some property, or minimization /
maximization in general, binary search can be one of the ways to solve it.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 11/15
7/9/24, 12:08 AM DailyCode
Link: https://codeforces.com/problemset/problem/676/C
Solution
We need to find the maximum length substring containing all equal characters, which is also
known as the beauty of the string. Let’s call it beauty number
Also, Vasya cannot change more than K characters of the string.
Okay, so let’s assume the beauty number is X. We want to determine what is the minimum
number of changes needed to make the string have a beauty number of X.
Let’s say G(X) returns this thing.
Now is F(X) monotonic. As X increases, i.e. we need a longer substring to have all characters
equal, which means more changes.
Similarly, as X decreases, we need to make all characters of a smaller substring equal, which
means less changes.
Thus, F(X) is monotonic, so we can binary search on X i.e. the beauty number or the length of
substring containing all equal characters.
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 12/15
7/9/24, 12:08 AM DailyCode
return changes;
}
Now once G(x) is calculated, we can just write F(x) = G(x) ≤ k. And then do a binary search for
X. The answer will be the last occurrence of True of F(x).
Code
#include<bits/stdc++.h> Copy
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 13/15
7/9/24, 12:08 AM DailyCode
return changes;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n, k;
cin>>n>>k;
string s;
cin>>s;
int lo = 0, hi = n+1;
cout<<lo<<"\n";
return 0;
}
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 14/15
7/9/24, 12:08 AM DailyCode
https://projects.100xdevs.com/pdf/dsa-9/Binary-search--two-sum-and-strings-1 15/15