From af33ba17176a8c277759f6d0d5c76735028b5c58 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:40:09 +0530 Subject: [PATCH 01/32] Update searching.md --- searching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searching.md b/searching.md index ed3c070..ccc3636 100644 --- a/searching.md +++ b/searching.md @@ -26,7 +26,7 @@ for (int i = 0; i < (1<>= 1; } // process permutation } From f945ea6a37e28b5560fc09db079dd515456198aa Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:53:48 +0530 Subject: [PATCH 02/32] Update searching.md --- searching.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/searching.md b/searching.md index ccc3636..f2e08d6 100644 --- a/searching.md +++ b/searching.md @@ -56,6 +56,37 @@ do { // Process permutation } while(next_permutation(permutation.begin(), permutation.end())); + + +// std implementation of next_permutation +bool nextPermutation(std::string &s, int n) +{ + // Find the largest index `i` such that `s[i-1]` is less than `s[i]` + int i = n-1; + while (s[i-1] >= s[i]) + { + // if `i` is the first index of the string, we are already at the last + // possible permutation (string is sorted in reverse order) + if (--i == 0) { + return false; + } + } + + // If we reach here, substring `s[i…n-1]` is sorted in reverse order. + // Find the highest index `j` to the right of index `i` such that `s[j] > s[i-1]`. + int j = n-1; + while (j > i && s[j] <= s[i-1]) { + j--; + } + + // swap character at index `i-1` with index `j` + std::swap(s[i-1], s[j]); + + // Reverse substring `s[i…n-1]`and return true + std::reverse (s.begin() + i, s.end()); + + return true; +} ``` ## Backtracking From 29479e150ef60430f71f63537f1b8b83822fcbae Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:27:58 +0530 Subject: [PATCH 03/32] Update sorting.md --- sorting.md | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/sorting.md b/sorting.md index 873dd45..40539f6 100644 --- a/sorting.md +++ b/sorting.md @@ -427,38 +427,34 @@ public: ### [Insert Interval In a Sorted List](https://leetcode.com/problems/insert-interval/) -In 2 pass found start and end bound. Start is max index with intervals\[start\]\[1\] <= newInterval\[0\] and end is min index with intervals\[end\]\[0\] <= newInterval\[1\] +Insert the new interval in the intervals array at correct position, shifting all the older items. Then perform approach from Merge Intervals problem ```cpp class Solution { public: vector> insert(vector>& intervals, vector& newInterval) { - if (intervals.empty()) return {newInterval}; - int l = 0, r = intervals.size()-1; - while (l <= r) - { - int mid = (l+r)/2; - if (intervals[mid][1] == newInterval[0]) { l = mid; break; } - if (intervals[mid][1] < newInterval[0]) l = mid+1; - else r = mid-1; + vector> res; + int start = 0; + for (int i = intervals.size()-1; i >= 0; --i) { + if (newInterval[0] > intervals[i][0]) { + start = i+1; + break; + } } - int start = l; r = intervals.size()-1; - while (l <= r) - { - int mid = (l+r)/2; - if (intervals[mid][0] == newInterval[1]) { r = mid; break; } - if (intervals[mid][0] < newInterval[1]) l = mid+1; - else r = mid-1; + + intervals.push_back(newInterval); + for (int i = intervals.size()-1; i > start; --i) { + swap(intervals[i], intervals[i-1]); } - int end = r; - if (start < intervals.size()) - newInterval[0] = min(newInterval[0], intervals[start][0]); - if (end >= 0) - newInterval[1] = max(newInterval[1], intervals[end][1]); - intervals.erase(intervals.begin()+start, intervals.begin()+end+1); - intervals.insert(intervals.begin()+start, newInterval); - return intervals; + res.push_back(intervals[0]); + for (int i = 1; i < intervals.size(); ++i) { + if (intervals[i][0] <= res.back()[1]) + res[res.size()-1][1] = max(res[res.size()-1][1], intervals[i][1]); + else + res.push_back(intervals[i]); + } + return res; } }; ``` From cfa3f51148e1696206b1acfa0a7813ada0b3950c Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sat, 9 Sep 2023 01:35:31 +0530 Subject: [PATCH 04/32] Update monotonic-structures.md --- monotonic-structures.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/monotonic-structures.md b/monotonic-structures.md index 3e1e254..0672dd7 100644 --- a/monotonic-structures.md +++ b/monotonic-structures.md @@ -1111,30 +1111,32 @@ int numSubarrayProductLessThanK(vector& nums, int k) ### [Largest Area in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) -Brute force is to pick two points, find minimum from i to j area within that bound will be min \* \(j-i+1\) just find maximum over all N square i j pairs. Complexity will be N^3 we can optimize it easily to get N^2. - -[https://youtu.be/MhQPpAoZbMc](https://youtu.be/MhQPpAoZbMc) +At a node i, we can brute force to find in left what is nearest smaller and in right what is nearest smaller. This can be further optimized through monotic stack. ```cpp -int largestRectangleArea(vector& heights) -{ +int largestRectangleArea(vector& heights) { + int n = heights.size(); + vector previousSmallest(n); + vector nextSmallest(n); stack st; - int ans = 0; - heights.push_back(-1); - for (int i = 0; i < heights.size(); ++i) - { - while (!st.empty() && heights[i] <= heights[st.top()]) - { - int height = heights[st.top()]; - st.pop(); - // i is rightmost smaller element, st.top() is - // prev top leftmost - int width = i - (st.empty() ? -1 : st.top()) - 1; - ans = max(ans, height * width); - } + for (int i = 0; i < n; ++i) { + while (!st.empty() && heights[st.top()] >= heights[i]) st.pop(); + previousSmallest[i] = st.empty() ? -1 : st.top(); st.push(i); } - return ans; + st = stack(); + for (int i = n-1; i >= 0; --i) { + while (!st.empty() && heights[st.top()] >= heights[i]) st.pop(); + nextSmallest[i] = st.empty() ? n : st.top(); + st.push(i); + } + + int res = 0; + for (int i = 0; i < n; ++i) { + int area = (nextSmallest[i] - previousSmallest[i] - 1) * heights[i]; + res = max(area, res); + } + return res; } ``` From b3db5d23bd5974750bfbdd0ba255022852ec6ecb Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sat, 9 Sep 2023 17:09:07 +0530 Subject: [PATCH 05/32] Update monotonic-structures.md --- monotonic-structures.md | 74 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/monotonic-structures.md b/monotonic-structures.md index 0672dd7..3afd232 100644 --- a/monotonic-structures.md +++ b/monotonic-structures.md @@ -1241,9 +1241,79 @@ string removeKdigits(string num, int k) } ``` -TODO: - * **LC739. Daily Temperatures** + +```c++ +vector dailyTemperatures(vector& temperatures) { + stack st; + vector res(temperatures.size()); + for (int i = temperatures.size()-1; i >= 0; --i) { + while (!st.empty() && temperatures[i] >= temperatures[st.top()]) st.pop(); + res[i] = st.empty() ? 0 : (st.top() - i); + st.push(i); + } + return res; +} +``` + * **LC901. Online Stock Span** + +```c++ +/** +find index of first number strictly greater than current +100 80 60 70 60 75 85 +1 1 1 2 1 4 6 +*/ +stack> st; +int sz; +StockSpanner() { + st = stack>(); + sz = 0; +} + +int next(int price) { + while (!st.empty() && st.top().second <= price) st.pop(); + int pt = st.empty() ? -1 : st.top().first; + int res = sz - pt; + st.push({sz++, price}); + return res; +} +``` + * **LC907. Sum of Subarray Minimums** +```c++ +int sumSubarrayMins(vector& arr) { + /* + 3 - [3] + 1 - [1], [1, 2], [1, 2, 4], [3, 1, 2], [3, 1], [3, 1, 2, 4] -> {0, 4} + 2 - [2], [2, 4] + 4 - [4] + */ + int n = arr.size(); + vector leftSmallest(n); + vector rightSmallest(n); + stack st; + for (int i = 0; i < n; ++i) { + while (!st.empty() && arr[i] < arr[st.top()]) st.pop(); + leftSmallest[i] = st.empty() ? -1 : st.top(); + st.push(i); + } + st = stack(); + for (int i = n-1; i >= 0; --i) { + // <= is required here for this case [71,55,82,55] otherwise it gives 483 whereas expected is 593 + while (!st.empty() && arr[i] <= arr[st.top()]) st.pop(); + rightSmallest[i] = st.empty() ? n : st.top(); + st.push(i); + } + long long res = 0; + const int MOD = 1e9 + 7; + for (int i = 0; i < n; ++i) { + int l = leftSmallest[i]; + int r = rightSmallest[i]; + cout << l << " " << r << "\n"; + res = (res + (((long long)arr[i] * (i-l) * (r-i)) % MOD)) % MOD; + } + return res; +} +``` From c0537100ba47a4dc3063657a6427c10744c47c86 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:31:38 +0530 Subject: [PATCH 06/32] Update dynamic-programming.md --- dynamic-programming.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dynamic-programming.md b/dynamic-programming.md index 689a128..0964b65 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -612,11 +612,20 @@ We have rod of length 5 with pieces value: 2, 5, 7, 8 for /* [3, 10, 2, 1, 20] = [3, 10, 20] = 3 [3, 2] = [3] or [2] = 1 [50, 3, 10, 7, 40, 50] = [3, 7, 40, 50] or [3, 10, 40, 50] = 4 */ -// N squared solution is very easy to have +// N squared solution is very easy to have using dp, dp[i] = min of dp[j]+1 where j < i and arr[j] < arr[i] -/* NlogN approach: Length of st denotes LIS formed by including -currently encountered element and maintaining an analogous LIS -behavior. */ +/* Greedy approach: +[3, 10, 2, 1, 20] +itterate and form increasing lists at the end of itteration we will have +list1: [3, 10, 20] list2: [2, 20] list3: [1, 20] so list1 is the answer. + +directly implementing above logic can be costly, there's an observation in it. +- We only care about the size of list not the actual list. +- To keep maintaining the list we need to just maintain relative order +so can we maintain all the lists in one list (denoted using set) ? length of list is the answer + +final algo: itterate, if set contain element atleast that then remove it and add the element. + */ int lengthOfLIS(vector& nums) { set st; From 9c237136ae036310aff61dce5edfab7265f1787a Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:58:02 +0530 Subject: [PATCH 07/32] Update dynamic-programming.md --- dynamic-programming.md | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/dynamic-programming.md b/dynamic-programming.md index 0964b65..82970fe 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -2098,28 +2098,29 @@ classical one path problem stores states like: dp[r][c] is maximum cherries that can be picked at r row and c cloumn. Now we have r1, c1, r2, c2. At any point of traversal r1 + c1 = r2 + c2 using this we can avoid one term -so, r2 = r1 + c1 - c2 -now we can form simmilar dp[r1][c1][c2] */ -int n, dp[55][55][55]; -int compute(vector> &grid, int r1, int c1, int c2) -{ - int r2 = r1+c1-c2; - if (r1 == n || r2 == n || c1 == n || c2 == n || grid[r1][c1] == -1 || grid[r2][c2] == -1) return INT_MIN; - if (r1 == n-1 && c1 == n-1) return grid[r1][c1]; - if (dp[r1][c1][c2] != INT_MIN) return dp[r1][c1][c2]; - - int ans = grid[r1][c1]; - if (c1 != c2) ans += grid[r2][c2]; - ans += max({compute(grid, r1, c1+1, c2+1), compute(grid, r1+1, c1, c2+1), - compute(grid, r1, c1+1, c2), compute(grid, r1+1, c1, c2)}); - dp[r1][c1][c2] = ans; - return ans; -} -int cherryPickup(vector>& grid) -{ - n = grid.size(); - for (int i = 0; i < 55; ++i) for (int j = 0; j < 55; ++j) for (int k = 0; k < 55; ++k) dp[i][j][k] = INT_MIN; - return max(0, compute(grid, 0, 0, 0)); +so, c2 = r1 + c1 - r2 +now we can form simmilar dp[r1][c1][r2] */ + +vector>> dp; +int solve(vector>& grid, int r1 = 0, int c1 = 0, int r2 = 0) { + int c2 = r1 + c1 - r2; + int n = grid.size(); + if (r1 == n || r2 == n || c1 == n || c2 == n || grid[r1][c1] == -1 || grid[r2][c2] == -1) return INT_MIN; + if (r1 == n-1 && c1 == n-1 && r2 == n-1 && c2 == n-1) return grid[n-1][n-1]; + if (dp[r1][c1][r2] != -1) return dp[r1][c1][r2]; + + int res = grid[r1][c1]; + + if (r1 != r2 || c1 != c2) res += grid[r2][c2]; + // DD DR RD RR + int incr = max({solve(grid, r1+1, c1, r2+1), solve(grid, r1+1, c1, r2), + solve(grid, r1, c1+1, r2+1), solve(grid, r1, c1+1, r2)}); + return dp[r1][c1][r2] = res + incr; +} +int cherryPickup(vector>& grid) { + int n = grid.size(); + dp.assign(n+1, vector>(n+1, vector(n+1, -1))); + return max(0, solve(grid)); } ``` From d8846bbafbaed1750f0da6c70fd83620b3fa361b Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:49:26 +0530 Subject: [PATCH 08/32] Update bit-manipulation.md --- bit-manipulation.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bit-manipulation.md b/bit-manipulation.md index 716188d..2cd884a 100644 --- a/bit-manipulation.md +++ b/bit-manipulation.md @@ -666,6 +666,26 @@ here is A is hexadecimal 1010 where 1 is present at every even position where as in power of 4 , there should be only one set bit that too in the odd position ``` +## Trick + +``` +Sometimes there's a number used as bit map representing state. Let's say 26 (11010) +now we want different combinations of set bit with this. + +26 11010 +24 11000 +18 10010 +16 10000 +10 01010 +8 01000 +2 00010 + +for (int i = x; i; i = x&(i-1)) { + cout << i << " "; + // 26 24 18 16 10 8 2 +} +``` + ## Conversion Write a function to determine the number of bits you would need to flip to convert A to integer B From a41e6a57237a91a0338b1a231c97872eaf6e20f1 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sat, 16 Dec 2023 16:03:49 +0530 Subject: [PATCH 09/32] Update dynamic-programming.md --- dynamic-programming.md | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/dynamic-programming.md b/dynamic-programming.md index 82970fe..d42280a 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -2446,6 +2446,61 @@ int checkRecord(int n) } ``` +### Optimal Account Balancing (Leetcode) +Implement splitwise simplify debts + +```cpp +/* +Idea is to first create a list of how much a person is owed and how much they owe. +Its a 1D list where + means ith person will get that amount and - means ith person will give that amount. +This array can be computed by essentially itterating through all the virtual transaction edges. + +Now the problem is basically optimally matching lenders with recievers. Folks with zero net can be removed from transaction +It can be solved through DP, maintain a bitwise state where ith bit set means transaction is pending there. Base case is 0 +which means no pending transaction. +*/ + +#include +using namespace std; + +int main() { + int n; cin >> n; + vector> arr(n, vector(3)); + for (auto &x : arr) + cin >> x[0] >> x[1] >> x[2]; + + vector debts(50, 0); + for (auto &x: arr) { + debts[x[0]] -= x[2]; + debts[x[1]] += x[2]; + } + + vector unresolvedDebts; + for (int x: debts) { + if (x != 0) + unresolvedDebts.push_back(x); + } + + int total = unresolvedDebts.size(); + function solve = [&](int state) -> int { + if (state == 0) return 0; + + int total = 0; + for (int i = 0; i < total; ++i) + total += (1< Date: Mon, 18 Dec 2023 01:58:47 +0530 Subject: [PATCH 10/32] Update sorting.md --- sorting.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/sorting.md b/sorting.md index 40539f6..b2ccbf0 100644 --- a/sorting.md +++ b/sorting.md @@ -199,25 +199,26 @@ sort is better */ ### Kth Smallest/Largest Number ```cpp -// Quick Sort extension -using namespace std; -int partition(int arr[], int l, int r) -{ - int pivot = arr[r]; - int j = l - 1; - for (int i = l; i <= r - 1; ++i) - if (arr[i] <= pivot) swap(arr[++j], arr[i]); - swap(arr[++j], arr[r]); - return j; -} -int k_th_smallest(int arr[], int l, int r, int k) -{ - if (k > 0 && k <= r - l + 1) - { - int pos = partition(arr, l, r); - if (pos - l == k - 1) return arr[pos]; - if (pos - l > k - 1) return k_th_smallest(arr, l, pos - 1, k); - return k_th_smallest(arr, pos + 1, r, k - pos + l - 1); +int findKthLargest(vector& nums, int k) { + srand((unsigned int)time(NULL)); + function partition = [&](int l, int r) -> int { + int partitionIndex = (rand() % (r - l + 1)) + l; + int pivot = nums[partitionIndex]; + swap(nums[partitionIndex], nums[r]); + int j = l; + for (int i = l; i < r; ++i) { + if (nums[i] < pivot) swap(nums[j++], nums[i]); + } + swap(nums[j], nums[r]); + return j; + }; + + int l = 0, r = nums.size() - 1; + while (l <= r) { + int partitionIndex = partition(l, r); + if (partitionIndex == nums.size() - k) return nums[partitionIndex]; + else if (partitionIndex > nums.size() - k) r = partitionIndex - 1; + else l = partitionIndex + 1; } return INT_MAX; } From 325d501b424a900af0ed637eb7b8f7598757200b Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 03:07:18 +0530 Subject: [PATCH 11/32] Update sorting.md --- sorting.md | 83 +++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/sorting.md b/sorting.md index b2ccbf0..0020325 100644 --- a/sorting.md +++ b/sorting.md @@ -663,51 +663,46 @@ Can be solved using trie along with Max XOR Pair problem ```cpp /* No need to check for isTerminal since we have same length (i.e. 32) for all */ -vector> trieArr; -int nxt; -void insert(int num) -{ - int pos = 0; - for (int i = 31; i >= 0; --i) - { - bool cur = !!(num & (1<= 0; --i) - { - bool cur = !!(num & (1<& nums) { + if (nums.size() <= 1) return 0; + + int nxt = 1; + function insert = [&](int toInsert) { + int node = 0; + for (int i = 31; i >= 0; --i) { + bool isIthBitSet = toInsert&(1< findOptimal = [&](int source) -> int { + int res = 0; + int node = 0; + for (int i = 31; i >= 0; --i) { + bool isIthBitSet = source&(1<& nums) -{ - trieArr.assign((nums.size()+1)*32, vector(2, 0)); - nxt = 1; - - for (auto &x : nums) insert(x); - int res = 0; - for (auto &x : nums) res = max(res, x^findOptimal(x)); - return res; -} +}; ``` ### [The Skyline Problem](https://leetcode.com/problems/the-skyline-problem/) From ba37286b750f6361ae768d48f066b6d3eb2f9b5d Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:26:45 +0530 Subject: [PATCH 12/32] Update searching.md --- searching.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/searching.md b/searching.md index f2e08d6..2b7b77a 100644 --- a/searching.md +++ b/searching.md @@ -57,6 +57,14 @@ do // Process permutation } while(next_permutation(permutation.begin(), permutation.end())); +/* +1 2 3 +1 3 2 +2 1 3 +2 3 1 +3 1 2 +3 2 1 +*/ // std implementation of next_permutation bool nextPermutation(std::string &s, int n) From 13b4bf1024384d7c607a0dadc2371d9a5f115f98 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:28:03 +0530 Subject: [PATCH 13/32] Update searching.md --- searching.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/searching.md b/searching.md index 2b7b77a..dd9ff95 100644 --- a/searching.md +++ b/searching.md @@ -66,6 +66,34 @@ do 3 2 1 */ +/* +1 2 3 4 5 +1 2 3 5 4 +1 2 4 3 5 +1 2 4 5 3 +1 2 5 3 4 +1 2 5 4 3 +1 3 2 4 5 +1 3 2 5 4 +1 3 4 2 5 +1 3 4 5 2 +1 3 5 2 4 +1 3 5 4 2 +1 4 2 3 5 +1 4 2 5 3 +1 4 3 2 5 +1 4 3 5 2 +1 4 5 2 3 +1 4 5 3 2 +1 5 2 3 4 +1 5 2 4 3 +1 5 3 2 4 +1 5 3 4 2 +1 5 4 2 3 +1 5 4 3 2 +... +*/ + // std implementation of next_permutation bool nextPermutation(std::string &s, int n) { From 0a0b236c11cf9300916b7ebd553cbacd9d9d6063 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:23:04 +0530 Subject: [PATCH 14/32] Update searching.md --- searching.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/searching.md b/searching.md index dd9ff95..83e7c77 100644 --- a/searching.md +++ b/searching.md @@ -172,7 +172,58 @@ Let us consider the problem of calculating the number of paths in an n x n grid Meet in the middle is a technique where the search space is divided into two parts of about equal size. A separate search is performed for both of the parts, and finally the results of the searches are combined. -For example, suppose that the list is \[2,4,5,9\] and x = 15. First, we divide the list into A = \[2,4\] and B = \[5,9\]. After this, we create lists SA = \[0,2,4,6\] and SB = \[0,5,9,14\]. \(SA is total subset sum possible in A\) Next we can find original problem solution by using hashmap SA\[i\] + SA\[j\] = targ. +### [Partition Array Into Two Arrays to Minimize Sum Difference](https://leetcode.com/problems/partition-array-into-two-arrays-to-minimize-sum-difference/description/) + +```cpp +/** Here naive solution was to get all possible subsets through bitmanipulation 2^(2n) for the cases where popcount is n get the sum. +However total time complexity of 2^2n is 2^30 will give TLE. + +Solution use meet in middle approach, divide the array in two part [0, n/2 - 1] [n/2, n-1] +Now, find all subset which will be 2*2^n i.e. 2^16 which is within limit. Grouping those subsets in terms of how many elements taken. +Now do a loop on how much to take from left and get its possible sums and do a lower bound on right possibilities. + + */ +class Solution { +public: + int minimumDifference(vector& nums) { + int totalSum = accumulate(nums.begin(), nums.end(), 0), n = nums.size(); + + function>(int, int)> findAllSubsets = [&](int l, int r) -> vector> { + int len = r - l + 1; + vector> res(len + 1); + for (int i = 0; i < (1 << len); ++i) { + int sum = 0; + for (int j = 0; j < len; ++j) { + if (i&(1< Date: Mon, 18 Dec 2023 19:23:29 +0530 Subject: [PATCH 15/32] Update searching.md --- searching.md | 147 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/searching.md b/searching.md index 83e7c77..de507c0 100644 --- a/searching.md +++ b/searching.md @@ -727,80 +727,109 @@ int search(vector &nums, int target) ### [Exam Room](https://leetcode.com/problems/exam-room/) ```c++ // O(N) seat and O(logN) leave -int numOfStudents; +int totalSeats; set locations; -ExamRoom(int N) : numOfStudents(N) { } + +ExamRoom(int N): totalSeats(N) { } void leave(int p) { locations.erase(p); } -int seat() -{ - int dist = 0, studentToSit = 0; - if (!locations.empty()) - { - auto beg = locations.begin(); - if (*beg != 0) dist = *beg; - for (auto it = next(beg); it != locations.end(); ++it) - { - int curDist = (*it - *beg)/2; - if (curDist > dist) dist = curDist, studentToSit = *beg + dist; - beg = it; +int seat() { + int posToUse = 0; + if (!locations.empty()) { + int optimalDistance = *locations.begin(); + for (auto i = locations.begin(), j = next(locations.begin()); j != locations.end(); ++i, ++j) { + int distance = (*j - *i) / 2; + if (distance > optimalDistance) + optimalDistance = distance, posToUse = *i + distance; } - beg = prev(locations.end()); - if (numOfStudents - *beg - 1 > dist) studentToSit = numOfStudents-1; + int lastDistance = totalSeats - *prev(locations.end()) - 1; + if (lastDistance > optimalDistance) + optimalDistance = lastDistance, posToUse = totalSeats - 1; } - locations.insert(studentToSit); - return studentToSit; + locations.insert(posToUse); + return posToUse; } // O(logN) seat and O(logN) leave -int n; -set cur; // stores point at which student is placed -set, greater>> cand; // stores [min_dist, -point] pair for possible future candidates. Pick one with highest distance and lowest index. -ExamRoom(int N) : n(N) { } -void addCandidate(int a, int b) -{ - int nextInd = (a + b)/2; - if (nextInd != a) cand.emplace(min(abs(b - nextInd), abs(a - nextInd)), -nextInd); +int totalSeats; +set locations; +// This set stores the future possibilities, idea is when we place a student there are +// two new possibilities, left or right that we create. pair stores [min_dist, -point] +// Pick one with highest distance and lowest index. +set, greater>> possibilities; + +// Given two locations first & second, add candidate in the middle. +void addCandidate(int first, int second) { + int nextIndex = (first + second) / 2; + if (nextIndex != first) + possibilities.insert({min(abs(first - nextIndex), abs(second - nextIndex)), -nextIndex}); } -void deleteCandidate(int a, int b) + +// Given two locations first & second, add candidate in the middle. +void deleteCandidate(int first, int second) { - int nextInd = (a + b)/2; - if (nextInd != a) cand.erase({min(abs(b - nextInd), abs(a - nextInd)), -nextInd}); + int nextIndex = (first + second) / 2; + if (nextIndex != first) + possibilities.erase({min(abs(first - nextIndex), abs(second - nextIndex)), -nextIndex}); } -int seat() -{ - if (cur.empty()) { cur.insert(0); cand.emplace(n-1, -(n-1)); return 0; } - // Place student on highest distance i.e. begin of cand, then placing new means breaking candidate area into two so adding them - auto [len, ind] = *cand.begin(); - ind = -ind; - cand.erase(cand.begin()); - auto nxt = cur.upper_bound(ind); - if (nxt != cur.end()) addCandidate(ind, *nxt); - auto prev = cur.upper_bound(ind); - if (prev != cur.begin()) { prev--; addCandidate(*prev, ind); } - cur.insert(ind); - return ind; +ExamRoom(int N): totalSeats(N) { } +int seat() { + if (locations.empty()) { + locations.insert(0); + possibilities.insert({totalSeats-1, -(totalSeats-1)}); + return 0; + } + + // Take the best possibility, and place the student there. + // After taking the possbility, it cannot be used in future. + auto [minDistance, point] = *possibilities.begin(); + point *= -1; + possibilities.erase(possibilities.begin()); + + // Taken choice, divides the vacant seats in two parts add them as possibility. + auto upperBoundLocation = locations.upper_bound(point); + if (upperBoundLocation != locations.end()) addCandidate(point, *upperBoundLocation); + if (upperBoundLocation != locations.begin()) addCandidate(point, *prev(upperBoundLocation)); + + locations.insert(point); + return point; } -void leave(int p) -{ - cur.erase(p); - if (cur.empty()) { cand.clear(); return; } - int nextInd = -1, prevInd = -1; - auto nxt = cur.upper_bound(p); - if (nxt != cur.end()) { nextInd = *nxt; deleteCandidate(p, *nxt); } - auto prev = cur.upper_bound(p); - if (prev != cur.begin()) { prev--, prevInd = *prev; deleteCandidate(*prev, p); } - if (cur.size() == 1) + +void leave(int p) { + locations.erase(p); + if (locations.empty()) { + possibilities.clear(); + return; + } + + // If a student leaves, it removes old small possibilities that were in left and right side. + // Instead a new bigger possibility comes in that place. + int nextIndex = -1, prevIndex = -1; + auto upperBoundLocation = locations.upper_bound(p); + if (upperBoundLocation != locations.end()) { + nextIndex = *upperBoundLocation; + deleteCandidate(nextIndex, p); + } + if (upperBoundLocation != locations.begin()) { + prevIndex = *prev(upperBoundLocation); + deleteCandidate(prevIndex, p); + } + + // If initially there were two students one left now new set of possibilities exist at left & right + // side of that remaining student. + if (locations.size() == 1) { - cand.clear(); - int ind = *cur.begin(); - if (ind != 0) cand.emplace(ind, 0); - if (ind != n-1) cand.emplace(n-1-ind, -(n-1)); + possibilities.clear(); + int ind = *locations.begin(); + if (ind != 0) possibilities.insert({ind, 0}); + if (ind != totalSeats-1) possibilities.insert({totalSeats - 1 - ind, -(totalSeats-1)}); return; } - if (p == 0) cand.emplace(*cur.begin(), 0); - else if (p == n-1) cand.emplace(n-1-*cur.rbegin(), -(n-1)); - else if (nextInd >= 0 && prevInd >= 0) addCandidate(prevInd, nextInd); + + // Need three cases 1) extreme left, 2) extreme right, and 3) middle. + if (p == 0) possibilities.insert({*locations.begin(), 0}); + else if (p == totalSeats-1) possibilities.insert({totalSeats - 1 - *locations.rbegin(), -(totalSeats-1)}); + else if (nextIndex >= 0 && prevIndex >= 0) addCandidate(prevIndex, nextIndex); } ``` From 774bee6efd5d69b96fb7fcd399ffed752477c6dd Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:38:19 +0530 Subject: [PATCH 16/32] Update searching.md --- searching.md | 67 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/searching.md b/searching.md index de507c0..24b9ffa 100644 --- a/searching.md +++ b/searching.md @@ -902,35 +902,48 @@ treat it like a matrix and then simply do it. */ ### [Median of two sorted arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/) ```cpp -class Solution { -public: - double solve(const vector &A, const vector &B, int l, int r, int count) - { - while (l < r) - { - // using mid = (l+r)/2 can cause TLE in case [0,0,0,0,0] [-1,0,0,0,0,0,1] - // because division of negative i.e. (l+r)/2 can act in reverse sense of floor - // l + (r-l)/2 on the other hand divides positive r-l which is always positive - int mid = l + (r-l)/2; - int cnt = (upper_bound(A.begin(), A.end(), mid) - A.begin()) + - (upper_bound(B.begin(), B.end(), mid) - B.begin()); - if (cnt > count) r = mid; - else l = mid+1; +// We can merge both lists using merge sort merge in O(n + m) time and then +// find the median. Can be done in O(1) space since we only care about finding median. + +// Use two pointer, both initialized at zero and keep incrementing for smaller value +// until we reach middle position. Time: O(n + m) Space: O(1) + +// Using binary search, idea is we find a point in smallest (or equal) array. Such that +// left part before that point and in larger array we can also find a pos (mathematically) +// such that right part after pos combine and form the middle part that will give median. +double findMedianSortedArrays(vector& nums1, vector& nums2) { + if (nums1.size() > nums2.size()) return findMedianSortedArrays(nums2, nums1); + + int l = 0, r = nums1.size(); + while (l <= r) { + int mid = l + (r-l)/2; + // Find the partition of the larger array such that the sum of elements on + // left side of the partition in both arrays is half of the total elements. + int pos = ((nums1.size() + nums2.size() + 1) / 2) - mid; + + int left1 = (mid > 0) ? nums1[mid - 1] : INT_MIN; + int right1 = (mid < nums1.size()) ? nums1[mid] : INT_MAX; + int left2 = (pos > 0) ? nums2[pos - 1] : INT_MIN; + int right2 = (pos < nums2.size()) ? nums2[pos] : INT_MAX; + + // Verify if partition is valid by verifying if the largest number on the + // left side is smaller than the smallest number on the right side. + if (left1 <= right2 && left2 <= right1) { + return ((nums1.size() + nums2.size())&1) ? + max(left1, left2) : (double)(max(left1, left2) + min(right1, right2)) / 2; } - return l; - } - double findMedianSortedArrays(vector& nums1, vector& nums2) - { - int n = nums1.size(), m = nums2.size(); - if (n == 0 && m == 0) return 0; - else if (m == 0) return (n&1) ? nums1[n/2] : (nums1[n/2 - 1] + nums1[n/2])/(double)2; - else if (n == 0) return (m&1) ? nums2[m/2] : (nums2[m/2 - 1] + nums2[m/2])/(double)2; - - int l = min(nums1[0], nums2[0]), r = max(nums1.back(), nums2.back()); - return ((n+m)&1) ? solve(nums1, nums2, l, r, (n+m)/2) : - (solve(nums1, nums2, l, r, (n+m)/2 - 1) + solve(nums1, nums2, l, r, (n+m)/2)) / (double)2; + else if (left1 > right2) r = mid - 1; + else l = mid + 1; } -}; + + /* + Example for [1, 2, 3] - [1, 2, 5, 6] + l: 0, r: 4 mid: 1, pos: 3 l1: 1, r1: 2, l2: 5, r2: 6 X + l: 2, r: 4 mid: 3, pos: 1 l1: 3, r1: ∞, l2: 1, r2: 2 X + l: 2, r: 2 mid: 2, pos: 2 l1: 2, r1: 3, l2: 2, r2: 5 + */ + return 0; +} ``` ### [Painter's Partition Problem](https://www.interviewbit.com/problems/painters-partition-problem/) From 623380bb06fe912b5d40e3de8f2251ddd662e4de Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:14:44 +0530 Subject: [PATCH 17/32] Update searching.md --- searching.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/searching.md b/searching.md index 24b9ffa..f7c4620 100644 --- a/searching.md +++ b/searching.md @@ -1038,6 +1038,26 @@ double minmaxGasDist(vector &stations, int k) } ``` +### [Minimized Maximum of Products Distributed to Any Store](https://leetcode.com/problems/minimized-maximum-of-products-distributed-to-any-store/description/) +```cpp +int minimizedMaximum(int n, vector& quantities) { + function check = [&](int x) -> bool { + int cnt = 0; + for (int quantity: quantities) + cnt += ceil((double)quantity / x); + return cnt <= n; + }; + + int l = 1, r = *max_element(quantities.begin(), quantities.end()); + while (l < r) { + int mid = l + (r-l)/2; + if (check(mid)) r = mid; + else l = mid + 1; + } + return l; +} +``` + ## Codeforces * [Maximum Median](https://codeforces.com/contest/1201/problem/C): [https://codeforces.com/contest/1201/submission/76923324](https://codeforces.com/contest/1201/submission/76923324) From faf4014815af49e5c4221739e4ec854d63c2d1e4 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 22:45:11 +0530 Subject: [PATCH 18/32] Update string-algorithms.md --- string-algorithms.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/string-algorithms.md b/string-algorithms.md index 4b5a5fb..0c469f8 100644 --- a/string-algorithms.md +++ b/string-algorithms.md @@ -390,7 +390,7 @@ vector> group_identical_strings(vector const& s) ![](.gitbook/assets/image%20%2863%29%20%281%29.png) -### Number of different substrings in a string \(N^2 log N\) +### Number of different substrings or Number of unique substring in a string \(N^2 log N\) ```cpp int count_unique_substrings(string const& s) @@ -421,6 +421,7 @@ int count_unique_substrings(string const& s) } return cnt; } +// There's a better way using LCP & Suffix Array below. ``` ## Rolling Hashes and Bloom Filters @@ -629,8 +630,24 @@ vector constructLCP(string const& s, vector const& p) // string s } ``` -### Count number of different substrings +### Count number of different substrings or unique substrings ```c++ +/* +Calculate suffix array and LCP array first. +Example: "ABABBAB" +SA String LCP +5 AB 0 +0 ABABBAB 2 +2 ABBAB 2 +6 B 0 +4 BAB 1 +1 BABBAB 3 +3 BBAB 1 + +A substring is a prefix of a suffix. So problem now is calculate unique prefixes across suffixes. +For n chars it will be n*(n+1)/2 +There will be LCP[i] duplicates at every suffix level +*/ int numberOfDiffSubstrings(vector &lcp, string &s) { int n = s.size(); From 74aca6943bdf55d907da0a665338715de9b91721 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:12:09 +0530 Subject: [PATCH 19/32] Update string-algorithms.md --- string-algorithms.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/string-algorithms.md b/string-algorithms.md index 0c469f8..c3c38ce 100644 --- a/string-algorithms.md +++ b/string-algorithms.md @@ -655,8 +655,32 @@ int numberOfDiffSubstrings(vector &lcp, string &s) } ``` -### Longest Common Substring +### (LCS) Longest Common Substring + +https://codeforces.com/edu/course/2/lesson/2/5 + ```c++ +/* +Can be done using DP there will be two states pointer i and pointer j and each transition will be constant checking if pointer are equal or not. +This will be O(N*M) + + +Idea is for a given s and t string: aabba and baba we append like aabba#baba now create suffix array and lcp array +a 2 +a#baba 1 +aabba#baba 1 +aba 2 +abba#baba 1 +ba 2 +ba#baba 1 +baba 2 +bba#baba 1 + +1/2 represents where the string is comming from 1 means its string s and 2 means its string 2. +This can be determined is suffix array ith element index is greater than s size +If both i and i+1 are from same string and lcp (i.e. common between i and i+1 here in code indexing is one minus because 0th element lcp doesn't exist) +LCP symbolises a potential Longest common substring. +*/ string s, t; cin >> s >> t; string str = s + '#' + t; auto sa = constructSuffixArray(str); From f9f660d2f506b68dc18c8af41083cb2d7b1e75d0 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:23:55 +0530 Subject: [PATCH 20/32] Update string-algorithms.md --- string-algorithms.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/string-algorithms.md b/string-algorithms.md index c3c38ce..dde8403 100644 --- a/string-algorithms.md +++ b/string-algorithms.md @@ -854,9 +854,20 @@ int strStr(string haystack, string needle) return -1; } -// KMP Algorithm -// https://youtu.be/4jY57Ehc14Y -// lps[i] is length of longest proper prefix of str[0..i] which is also suffix of str[0..i] +/* +KMP Algorithm: https://youtu.be/4jY57Ehc14Y +ONIONIONSPL +we need to find ONIONS in it +if we search from index 0 everything except last char will match. We cannot simply increment i to current j pointer because then we +will miss ONIONS present in overlapping way. +We need this info that what suffix of previous found string is the prefix of target we are looking for. +Here lps[i] is length of longest proper prefix of str[0..i] which is also suffix of str[0..i] +^ By proper prefix we mean we cannot take whole string as it will be suffix too +In above example ON is the suffix in string ONION so we move pointer to 3 and we will find the target. + +we are pre calculating LPS array. + +*/ int strStr(string haystack, string needle) { if (needle.size() == 0) return 0; From 69137c6e91126920f97d1dbcdb69730de58c3f41 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:34:09 +0530 Subject: [PATCH 21/32] Update string-algorithms.md --- string-algorithms.md | 1 + 1 file changed, 1 insertion(+) diff --git a/string-algorithms.md b/string-algorithms.md index dde8403..c0689e2 100644 --- a/string-algorithms.md +++ b/string-algorithms.md @@ -909,6 +909,7 @@ Given an input text of length n and an array of k words, arr\[\], find all occur If we iterate over words and perform pattern matching complexity will be O\(nk + m\) with this algo it will be O\(n + m + z\) where z is total number of occurrences of word in the text. ## Z Algorithm +Its a 1D numeric array where Z[i] is length of maxium proper prefix of string which is also a prefix starting at i. ```cpp // N^2 version From fab0f53fe95f8767643ff3e90cb0c1dbb40eef7d Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:43:22 +0530 Subject: [PATCH 22/32] Update range-queries.md --- range-queries.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/range-queries.md b/range-queries.md index 1e21567..37fe104 100644 --- a/range-queries.md +++ b/range-queries.md @@ -23,10 +23,12 @@ public: }; ``` +## Difference Array +D[i] = A[i] - A[i-1] and D[0] = A[0] -## Prefix XOR +Now update(l, r, x) : Add x to D[l] and subtract it from D[r+1] -## Difference Array +After all queries simply A[0] = D[0], A[i] = A[i-1] + D[i] ## Sparse Table From 1c3130850837c53448ffc2e1578e34b77bc23db6 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:56:39 +0530 Subject: [PATCH 23/32] Update range-queries.md --- range-queries.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/range-queries.md b/range-queries.md index 37fe104..a8bd92d 100644 --- a/range-queries.md +++ b/range-queries.md @@ -124,7 +124,8 @@ signed main() ## Fenwick Tree It is used to calculate prefix sum of an array. It's not as powerful as Segment Tree but it's simpler to implement. - In the image, BIT will be stored in array of n+1 length. \(indexing starts with 1 because binary operation used later on will be anomoly for 0\) for each say 3 find 3 & -3 this will flip right most set bit giving the parent from tree. + In the image, BIT will be stored in array of n+1 length. \(indexing starts with 1 because binary operation used later on will be anomoly for 0\) for each say 3 find 3 & -3 this will flip right most set bit so for 3 (11) it gives 2 for 5 (101) it gives 1 (For 2 powered num it gives same number). + Adding it to existing i value will give the parent from tree (refer image). Now to fill all value say 4 we find 4 = 0 + 2^2 means we go from 0th index to next 4 elements or for 11 = 2^3 + 2^1 + 2^0 from 10-10 ![](res/BIT.png) If we need to find sum from 0 to 5 we first go to value 6 take it here it's 9 then we go to it's parent it's 10 so ans is 19 From 7170b661095c2043ba0ca42eddb8cc3a8620ca92 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:16:30 +0530 Subject: [PATCH 24/32] Update implementation-based.md --- implementation-based.md | 56 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/implementation-based.md b/implementation-based.md index ec63296..8bc78cb 100644 --- a/implementation-based.md +++ b/implementation-based.md @@ -272,13 +272,13 @@ If we access say 2 then it becomes 2 4 3 1 Now if we insert 5, 1 gets removed i.e. 5 2 4 3 this get & set function should be in O(1) */ class LRUCache { -public: - +private: struct Node { int key, val; Node *prev, *next; }; unordered_map rec; Node *head, *tail; int sz, maxCapacity; +public: LRUCache(int capacity) { head = NULL, tail = NULL; @@ -339,6 +339,58 @@ public: }; ``` +### [LFU Cache](https://leetcode.com/problems/lfu-cache/submissions/) + +```cpp +class LFUCache { +private: + struct Node { int key, value, freq; list::const_iterator it; }; + int maxCapacity, minFrequency; + unordered_map nodeByKey; + unordered_map> nodeKeysListByFrequency; + +public: + LFUCache(int capacity): maxCapacity(capacity), minFrequency(0) {} + int get(int key) { + if (nodeByKey.find(key) == nodeByKey.end()) return -1; + Node& node = nodeByKey[key]; + updateFrequency(node); + return node.value; + } + + void put(int key, int value) { + if (maxCapacity == 0) return; + if (nodeByKey.find(key) != nodeByKey.end()) { + Node& node = nodeByKey[key]; + node.value = value; + updateFrequency(node); + return; + } + if (nodeByKey.size() == maxCapacity) { + const int requiredKey = nodeKeysListByFrequency[minFrequency].back(); + nodeKeysListByFrequency[minFrequency].pop_back(); + nodeByKey.erase(requiredKey); + } + + minFrequency = 1; + nodeKeysListByFrequency[1].push_front(key); + nodeByKey[key] = {key, value, 1, cbegin(nodeKeysListByFrequency[1])}; + } + +private: + void updateFrequency(Node& node) { + int prevFreq = node.freq, newFreq = ++node.freq; + nodeKeysListByFrequency[prevFreq].erase(node.it); + if (nodeKeysListByFrequency[prevFreq].empty()) { + nodeKeysListByFrequency.erase(prevFreq); + if (prevFreq == minFrequency) ++minFrequency; + } + nodeKeysListByFrequency[newFreq].push_front(node.key); + node.it = cbegin(nodeKeysListByFrequency[newFreq]); + } +}; +``` + ### [Design Twitter](https://leetcode.com/problems/design-twitter/) ```cpp From 2d145c932108a5b86c164c44b7856e4019a1db56 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:27:50 +0530 Subject: [PATCH 25/32] Update implementation-based.md --- implementation-based.md | 53 ++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/implementation-based.md b/implementation-based.md index 8bc78cb..12eed2f 100644 --- a/implementation-based.md +++ b/implementation-based.md @@ -448,25 +448,6 @@ double findMedian() { return sz&1 ? arr[sz/2] : (arr[sz/2 + 1] + arr[sz/2])/2.0; } - -// O(logN) using 2 heaps -priority_queue l; -priority_queue, greater> r; -void addNum(int num) -{ - l.push(num); - r.push(l.top()); - l.pop(); - if (l.size() < r.size()) - { - l.push(r.top()); - r.pop(); - } -} -double findMedian() -{ - return l.size() > r.size() ? l.top() : ((double) l.top() + r.top()) * 0.5; -} ``` * Maintain two heaps i.e. left and right half from middle. We are performing 3 push and 2 pop operations making it total O\(5logN\) @@ -476,6 +457,7 @@ priority_queue left; priority_queue, greater> right; MedianFinder() { } +// LogN Solution /* Think 3 steps: sz(left) == sz(right), sz(left) > sz(right), sz(left) < sz(right) */ void addNum(int num) { @@ -498,20 +480,27 @@ double findMedian() * We can use policy based data structure, both insertion and find takes logN & 3logN time here but there's no overhead of inserting 5 times so this might perform better in some scenerios ```cpp -orderedMultiSet st; -int n = 0; +#include +#include +using namespace __gnu_pbds; +typedef tree, rb_tree_tag, tree_order_statistics_node_update> orderedMultiSet; -MedianFinder() { } -void addNum(int num) -{ - st.insert(num); - n++; -} -double findMedian() -{ - return n&1 ? *st.find_by_order(n/2) : - (*st.find_by_order(n/2 - 1) + *st.find_by_order(n/2))/2.0; -} +class MedianFinder { +public: + orderedMultiSet st; + int n = 0; + + MedianFinder() { } + void addNum(int num) + { + st.insert(num); + n++; + } + double findMedian() + { + return n&1 ? *st.find_by_order(n/2) : (*st.find_by_order(n/2 - 1) + *st.find_by_order(n/2))/2.0; + } +}; ``` ### [Design HashMap](https://leetcode.com/problems/design-hashmap/) From 819438ccf77af7d0786fd4549d88f6b4eadfc45b Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:40:37 +0530 Subject: [PATCH 26/32] Update implementation-based.md --- implementation-based.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/implementation-based.md b/implementation-based.md index 12eed2f..cee6a95 100644 --- a/implementation-based.md +++ b/implementation-based.md @@ -501,6 +501,19 @@ public: return n&1 ? *st.find_by_order(n/2) : (*st.find_by_order(n/2 - 1) + *st.find_by_order(n/2))/2.0; } }; +// Regular set uses a BST internally, red black tree uses a red black tree +// Regular set has this drawback that we cannot do random access, with policy based you can even do that in O(LogN) time +// order_of_key(k) number of items strictly smaller than k +// find_by_order(k) kth element from set counting from zero + +/* +A BST is simply whose left node is small and right large. But often BST can end up as skewed (like a linked list) +Solution is balanced trees like AVL or Red black tree which is a type of balanced tree. +- A node is either black or red. +- The root and nil are always black. +- If a node is red its childrens are black. +- All path from its nil descendants contains the same number of black nodes. +*/ ``` ### [Design HashMap](https://leetcode.com/problems/design-hashmap/) From 8bf7d58ec662e5212f77b9afc6bb73b7015c9ed1 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:21:18 +0530 Subject: [PATCH 27/32] Update dynamic-programming.md --- dynamic-programming.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dynamic-programming.md b/dynamic-programming.md index d42280a..256ff2d 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -117,6 +117,25 @@ int numWays(int n, int k) for (int i = 3; i <= n; ++i) dp[i] = (k-1) * (dp[i-1] + dp[i-2]); return dp[n]; } + +// Top down +int numWays(int n, int k) { + vector> dp(n, vector(k+1, -1)); + function solve = [&](int cur, int lastColor) -> int { + if (cur == n) return 1; + if (dp[cur][lastColor] != -1) return dp[cur][lastColor]; + + int res = 0; + for (int color = 1; color <= k; ++color) { + if (color == lastColor) continue; + res += solve(cur + 1, color); + if (cur + 2 <= n) res += solve(cur + 2, color); + } + return dp[cur][lastColor] = res; + }; + + return solve(0, 0); +} ``` ### Ugly Number From 95c2500cfe78e29d168209bd8fae6180371cdfd3 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sat, 23 Dec 2023 18:33:02 +0530 Subject: [PATCH 28/32] Update dynamic-programming.md --- dynamic-programming.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dynamic-programming.md b/dynamic-programming.md index 256ff2d..aebee4f 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -240,6 +240,36 @@ int numDecodings(string s) } // Variant - II +// Topdown +int numDecodings(string s) { + const int MOD = 1e9 + 7; + vector dp(s.size(), -1); + + function solve = [&](int cur) -> int { + if (cur == s.size()) return 1; + if (dp[cur] != -1) return dp[cur]; + + bool firstWild = s[cur] == '*'; + bool secondWild = cur + 1 < s.size() && s[cur + 1] == '*'; + + long long int res = 0; + // Taking 1 at a time. + res += firstWild ? 9 * solve(cur + 1) : (s[cur] > '0' ? solve(cur + 1) : 0); + // Taking 2 at a time. + if (cur + 1 < s.size()) { + // possibility when ** -> 11 12 13 14 15 16 17 18 19 21 22 23 24 25 26 + if (firstWild && secondWild) res += 15 * solve(cur + 2); + else if (firstWild && !secondWild) res += ((s[cur + 1] <= '6') ? 2 : 1) * solve(cur + 2); + else if (!firstWild && secondWild) res += (s[cur] == '0' || s[cur] > '2') ? 0 : + ((s[cur] == '1') ? 9 : 6) * solve(cur + 2); + else res += (s[cur] == '1' || (s[cur] == '2' && s[cur + 1] <= '6')) ? solve(cur + 2) : 0; + } + return dp[cur] = (res % MOD); + }; + + return solve(0); +} + // Bottom up const int M = 1e9 + 7; int numDecodings(string s) From a23ceeccdd4e353977d5ae6408d28b13ceafad67 Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sun, 24 Dec 2023 19:06:31 +0530 Subject: [PATCH 29/32] Update dynamic-programming.md --- dynamic-programming.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/dynamic-programming.md b/dynamic-programming.md index aebee4f..e2e3548 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -621,26 +621,27 @@ A(BC) = (30×5×60) + (10×30×60) = 27000 operations. 2 [0, 5x60] ``` -### Burst Balloon Problem +### [Burst Balloon Problem](https://leetcode.com/problems/burst-balloons/) -```text -Given values of balloons: 3 1 5 8 -Each bursting means that value * left * right value -We need to burst them such that max val: like in -order-5 1 3 8 (1*5*8 + 3*1*8 + 3*8 + 8 = 96) - -Diagonals represents first balloon which we burst. 0 1 means -balloons within 0 to 1 - 0 1 2 3 -0 3 30 159 167 -1 15 135 159 -2 40 48 -3 40 - -For [0, 2] value left [0, 1] denotes what if we choose [0, 1] to -burst first then we will burst 2 so for ever cell it's -dp[i][j] = max(dp[i][j-1] + arr[j]*arr[j+1], dp[i+1][j] + -arr[0]*arr[j+1]) +```cpp +int maxCoins(vector& nums) { + nums.insert(nums.begin(), 1); + nums.push_back(1); + int n = nums.size(); + vector> dp(n, vector(n, -1)); + + function solve = [&](int l, int r) -> int { + if (l > r) return 0; + if (dp[l][r] != -1) return dp[l][r]; + + int res = 0; + for (int i = l; i <= r; ++i) + res = max(res, (nums[l-1] * nums[i] * nums[r + 1]) + solve(l, i - 1) + solve(i + 1, r)); + return dp[l][r] = res; + }; + + return solve(1, n-2); +} ``` ### Rod Cutting Problem From 2673c59a09688d248e5fc7595c07036d1bd6105e Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sun, 24 Dec 2023 20:14:31 +0530 Subject: [PATCH 30/32] Update dynamic-programming.md --- dynamic-programming.md | 70 +++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/dynamic-programming.md b/dynamic-programming.md index e2e3548..1eaf8ec 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -656,37 +656,49 @@ We have rod of length 5 with pieces value: 2, 5, 7, 8 for 7 ``` -### Longest Increasing Subsequence +### Longest Increasing Subsequence (LIS) + +https://leetcode.com/problems/longest-increasing-subsequence/ ```cpp -/* [3, 10, 2, 1, 20] = [3, 10, 20] = 3 -[3, 2] = [3] or [2] = 1 -[50, 3, 10, 7, 40, 50] = [3, 7, 40, 50] or [3, 10, 40, 50] = 4 */ -// N squared solution is very easy to have using dp, dp[i] = min of dp[j]+1 where j < i and arr[j] < arr[i] - -/* Greedy approach: -[3, 10, 2, 1, 20] -itterate and form increasing lists at the end of itteration we will have -list1: [3, 10, 20] list2: [2, 20] list3: [1, 20] so list1 is the answer. - -directly implementing above logic can be costly, there's an observation in it. -- We only care about the size of list not the actual list. -- To keep maintaining the list we need to just maintain relative order -so can we maintain all the lists in one list (denoted using set) ? length of list is the answer - -final algo: itterate, if set contain element atleast that then remove it and add the element. - */ -int lengthOfLIS(vector& nums) -{ - set st; - for (int x : nums) - { - auto lb = st.lower_bound(x); - if (lb != st.end()) st.erase(lb); - st.insert(x); - } - for (auto &x : st) cout << x << " "; - return st.size(); +// N^2 solution using DP. +// dp[i] is the longest increasing subsequence ending at i. +int lengthOfLIS(vector& nums) { + int n = nums.size(); + vector dp(n, 1); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (nums[j] < nums[i]) + dp[i] = max(dp[i], dp[j] + 1); + } + } + return *max_element(dp.begin(), dp.end()); +} + +// Another approach could be. +// dp[i] now represents smallest element at which an increasing subsequence of length i ends. +int lengthOfLIS(vector& nums) { + int n = nums.size(); + const int INF = 1e9; + vector dp(n+1, INF); + dp[0] = -INF; + for (int i = 0; i < n; ++i) { + // This block can be optimized... + for (int l = 1; l <= n; ++l) { + if (dp[l-1] < nums[i] && nums[i] < dp[l]) + dp[l] = nums[i]; + } + // This block can be optimized. + // dp array is monotonically increasing, hence optimal l can be found simply by (no need to loop). + // int l = upper_bound(dp.begin(), dp.end(), nums[i]) - dp.begin(); + // Reducing time complexity from N^2 to NlogN + } + + int res = 0; + for (int l = 1; l <= nums.size(); ++l) { + if (dp[l] < INF) res = l; + } + return res; } /* If the problem was Find Long Increasing Bitonic subsequence, From ff1448d204fdf39bc5896a0ac00a7ddeb6f6f84d Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sun, 24 Dec 2023 20:52:59 +0530 Subject: [PATCH 31/32] Update dynamic-programming.md --- dynamic-programming.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/dynamic-programming.md b/dynamic-programming.md index 1eaf8ec..df0d5ee 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -727,26 +727,29 @@ cout << st.size() << '\n'; * [https://leetcode.com/problems/russian-doll-envelopes/](https://leetcode.com/problems/russian-doll-envelopes/) ```cpp -/* Follow up question do we have to take care of orientation? In that case -rotate envelopes initially such first w is small h is large always. */ - -/* N^2 LIS is easy to impliment, To do it in NlogN we require same LIS logic as before */ -int maxEnvelopes(vector>& envelopes) -{ - sort(envelopes.begin(), envelopes.end(), [](auto x, auto y) - { - return (x[0] == y[0]) ? (x[1] > y[1]) : (x[0] < y[0]); +/* This can be done using LIS. */ +int maxEnvelopes(vector>& envelopes) { + // Sort ascending on first and descending on second. Now if we take only second part + // It is guaranteed that first part is sorted because for equal we are taking descending order. + sort(envelopes.begin(), envelopes.end(), [](auto x, auto y) { + return x[0] == y[0] ? x[1] > y[1] : x[0] < y[0]; }); - vector> lis; - auto comp = [](auto x, auto y) { return (x[1] < y[1]) && (x[0] < y[0]); }; - for (const auto x : envelopes) - { - auto lb = lower_bound(lis.begin(), lis.end(), x, comp); - if (lb == lis.end()) lis.push_back(x); - else *lb = x; + vector nums; + for (auto x: envelopes) nums.push_back(x[1]); + int n = nums.size(); + vector dp(n+1, INT_MAX); + dp[0] = INT_MIN; + for (int i = 0; i < n; ++i) { + int l = upper_bound(dp.begin(), dp.end(), nums[i]) - dp.begin(); + if (dp[l-1] < nums[i] && nums[i] < dp[l]) + dp[l] = nums[i]; + } + int res = 0; + for (int i = 1; i <= n; ++i) { + if (dp[i] != INT_MAX) res = i; } - return lis.size(); + return res; } ``` From 9edbbefbbe8e02ca8cee61a96b22c7c4a641248f Mon Sep 17 00:00:00 2001 From: Ankit Priyarup <31309984+ankitpriyarup@users.noreply.github.com> Date: Sun, 24 Dec 2023 21:13:29 +0530 Subject: [PATCH 32/32] Update dynamic-programming.md --- dynamic-programming.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/dynamic-programming.md b/dynamic-programming.md index df0d5ee..10e3806 100644 --- a/dynamic-programming.md +++ b/dynamic-programming.md @@ -790,6 +790,45 @@ int candy(vector& ratings) return sm; } +// Graph based approach, add a directed edge between two adjacent students if rating is higher. +// Then simply run a topological sort to get the candy count. +int candy(vector& ratings) { + int n = ratings.size(); + vector adj[n]; + vector inDeg(n, 0); + for (int i = 1; i < n; ++i) { + if (ratings[i-1] > ratings[i]) { + adj[i].push_back(i-1); + inDeg[i-1]++; + } + if (ratings[i-1] < ratings[i]) { + adj[i-1].push_back(i); + inDeg[i]++; + } + } + + int res = 0; + queue q; + for (int i = 0; i < n; ++i) { + if (inDeg[i] == 0) q.push(i); + } + int candy = 1; + while (!q.empty()) + { + int sz = q.size(); + while (sz--) { + int u = q.front(); q.pop(); + res += candy; + for (int &v : adj[u]) { + inDeg[v]--; + if (inDeg[v] == 0) q.push(v); + } + } + candy++; + } + return res; +} + /* One pass, constant space appraoch Observation: Candies are always given increment of 1. Local minimum number of candies given to a student is 1 so subdistribution is of form 1, 2, 3, ..., n or