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 diff --git a/dynamic-programming.md b/dynamic-programming.md index 689a128..10e3806 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 @@ -221,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) @@ -572,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 @@ -606,28 +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 +// 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()); +} -/* NlogN approach: Length of st denotes LIS formed by including -currently encountered element and maintaining an analogous LIS -behavior. */ -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); +// 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 } - for (auto &x : st) cout << x << " "; - return st.size(); + + 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, @@ -656,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; } ``` @@ -716,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 @@ -2089,28 +2202,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)); } ``` @@ -2436,6 +2550,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< 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 @@ -396,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\) @@ -424,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) { @@ -446,20 +480,40 @@ 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; + } +}; +// 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/) diff --git a/monotonic-structures.md b/monotonic-structures.md index 3e1e254..3afd232 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; } ``` @@ -1239,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; +} +``` diff --git a/range-queries.md b/range-queries.md index 1e21567..a8bd92d 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 @@ -122,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 diff --git a/searching.md b/searching.md index ed3c070..f7c4620 100644 --- a/searching.md +++ b/searching.md @@ -26,7 +26,7 @@ for (int i = 0; i < (1<>= 1; } // process permutation } @@ -56,6 +56,73 @@ 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 +*/ + +/* +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) +{ + // 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 @@ -105,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< &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); -} -void deleteCandidate(int a, int b) -{ - int nextInd = (a + b)/2; - if (nextInd != a) cand.erase({min(abs(b - nextInd), abs(a - nextInd)), -nextInd}); -} - -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; -} -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) +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}); +} + +// Given two locations first & second, add candidate in the middle. +void deleteCandidate(int first, int second) +{ + int nextIndex = (first + second) / 2; + if (nextIndex != first) + possibilities.erase({min(abs(first - nextIndex), abs(second - nextIndex)), -nextIndex}); +} + +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) { + 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); } ``` @@ -755,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/) @@ -878,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) diff --git a/sorting.md b/sorting.md index 873dd45..0020325 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; } @@ -427,38 +428,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; } }; ``` @@ -666,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/) diff --git a/string-algorithms.md b/string-algorithms.md index 4b5a5fb..c0689e2 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(); @@ -638,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); @@ -813,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; @@ -857,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