diff --git a/LeetCode/Algorithms/CountSubsetsWithSumK.cpp b/LeetCode/Algorithms/CountSubsetsWithSumK.cpp new file mode 100644 index 0000000..8e43621 --- /dev/null +++ b/LeetCode/Algorithms/CountSubsetsWithSumK.cpp @@ -0,0 +1,32 @@ +int mod = (int)(1e9 + 7); +int f(vector &arr, int ind, int sum, vector> &dp) { + // if(sum == 0) return 1; + if(ind == 0) { + if(sum == 0 && arr[0] == 0) return 2; // 0 0 1 2 3, target = 5 - to handle case where multiple 0's + if(sum == 0) return 1; // + return arr[0] == sum; + // if(sum == 0 || sum == arr[0]) return 1; + // return 0; + } + if(dp[ind][sum] != -1) return dp[ind][sum]; + // not take + int notTake = f(arr, ind - 1, sum, dp); + + // take + int take = 0; + if(sum >= arr[ind]) { + take = f(arr, ind -1, sum - arr[ind], dp); + } + + return dp[ind][sum] = (take + notTake) % mod; +} + +int findWays(vector& arr, int k) +{ + // Write your code here. + int n = arr.size(); + vector> dp(n, vector(k + 1, -1)); + return f(arr, n-1, k, dp); +} + + diff --git a/LeetCode/Algorithms/DisjoinSet.cpp b/LeetCode/Algorithms/DisjoinSet.cpp new file mode 100644 index 0000000..e55db19 --- /dev/null +++ b/LeetCode/Algorithms/DisjoinSet.cpp @@ -0,0 +1,78 @@ +#include +using namespace std; +class DisjoinSet { + vector rank, parent, size; + +public: + DisjoinSet(int n) { + rank.resize(n + 1, 0); + size.resize(n + 1, 0); + parent.resize(n + 1); + for(int i=0; i<=n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + int findUPar(int node) { + if(node == parent[node]) { + return node; + } + return parent[node] = findUPar(parent[node]); + } + + void UnionByRank(int u, int v) { + int ulp_u = findUPar(u); + int ulp_v = findUPar(v); + + if(ulp_u == ulp_v) { + return; + } else if(rank[ulp_v] > rank[ulp_u]) { + parent[ulp_u] = ulp_v; + } else if(rank[ulp_u] > rank[ulp_v]) { + parent[ulp_v] = ulp_u; + } else if(rank[ulp_u] == rank[ulp_v]) { + parent[ulp_v] = ulp_u; + rank[ulp_v]++; + } + } + + void UnionBySize(int u, int v) { + int ulp_u = parent[u]; + int ulp_v = parent[v]; + + if(ulp_u == ulp_v) return; + if(size[ulp_u] > size[ulp_v]) { + size[ulp_u] += size[ulp_v]; + parent[ulp_v] = ulp_u; + } else { + size[ulp_v] += size[ulp_u]; + parent[ulp_u] = ulp_v; + } + } +}; + +int main() { + DisjoinSet ds(7); + ds.UnionByRank(1, 2); + ds.UnionByRank(2, 3); + ds.UnionByRank(4, 5); + ds.UnionByRank(6, 7); + ds.UnionByRank(5, 6); + // ds.UnionByRank(1, 2); + + // if 3 and 7 same or not + if(ds.findUPar(3) == ds.findUPar(7)) { + cout<<"Same"<start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + // struct myComp { + // bool operator() (Interval &a, Interval &b) { + // return a.start < b.start; + // } + // }; + + static bool myComp(const Interval &a, const Interval &b) { + return a.start < b.start; + } + + bool canAttendMeetings(vector& intervals) { + if(intervals.empty()) return true; + sort(intervals.begin(), intervals.end(), myComp); + + // sort(intervals.begin(), intervals.end(), myComp()); + + // sort(intervals.begin(), intervals.end(), [](Interval &a, Interval &b) { + // return a.start < b.start; + // }); + + int prevEnd = intervals[0].end; + + for(int i=1; i& nums, long long k) { + ll left = 0, right = 0; // window is [left, right) + ll sum = 0; // sum of nums[left ... right -1] + ll count = 0; + + ll n = nums.size(); + + while(left < n) { + // find larget valid window + while(right < n && (sum + nums[right] * (right - left + 1) < k)) { + sum += nums[right]; + right++; + } + + // All subarrays starting at 'left' and ending before 'right' are valid + count += right - left; + + // Slide the window forward by removing nums[left] + // If we couldn't even include nums[left], move both pointer past it + if(left == right) { + right++; + } else { + sum -= nums[left]; + } + + left++; + } + + return count; + } +}; + +/* + +Time Complexity - O(N) +Space Complexity - O(1) + +Counting Logic + 0 1 2 3 4 5 +a[]: [2, 1, 1, 3, 4, 1], k = 15 + | | + start end + +1. Increase end till sum * len < k +2. Count = end - start +3. Increase start pointer and again when sum * len >= k, + calculate count and increment start++ pointer + +[start, End) => largest subarray starting at '0' which has (sum * size) < k + +start, end - 1 ie 4 * 3 < 15 + +Count of such Subarrays start at '0' = 3 + +in general [start, start], [start, start+ 1], ... [start, end-1] + +count = end - start + +// Two Pointer + Sliding Window + + + +*/ \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/FindMinimumDiameterAfterMergingTwoTrees.cpp b/LeetCode/Algorithms/Hard/FindMinimumDiameterAfterMergingTwoTrees.cpp new file mode 100644 index 0000000..35504e9 --- /dev/null +++ b/LeetCode/Algorithms/Hard/FindMinimumDiameterAfterMergingTwoTrees.cpp @@ -0,0 +1,111 @@ +class Solution { +public: + int diameter(unordered_map>& adj, int n) { + // pick any node and find the farthest from that node + vector visited(n, false); + queue q; + q.push(0); + visited[0] = true; + int last; + while(!q.empty()) { + int size = q.size(); + for(int i=0; i vis(n, false); + vis[last] = true; + while(!q.empty()) { + int size = q.size(); + for(int i=0; i>& edges) { + if(edges.size() == 0) { + return 0; + } + + unordered_map> adj; + unordered_set nodes; + + for(auto &edge: edges) { + cout<>& edges1, vector>& edges2) { + int dia1 = findDiameter(edges1); + int dia2 = findDiameter(edges2); + + int radius1 = (dia1 + 1) / 2; + int radius2 = (dia2 + 1) / 2; + int sum = 1 + radius1 + radius2; + + return max(sum, max(dia1, dia2)); + } +}; + +// Time Complexity - O(N + M) +// Space Complexity - O(N + M) + +// Follow-up +// 1. Prove the greedy algo. +// 2. Find al diameters of a tree + +// How to find optimal path? + +// Note: It is always optimal to join at midpoint of diameter + +// total height = h1 / 2 + bridge (1) + h2 / 2 + +// Cases + +// Even-Even ==> ans = 1 + dia1 / 2 + dia2 / 2 +// Odd-Even ==> ans = 1 + (dia1 + 1) / 2 + dia2 / 2 +// Odd-Odd ==> ans = 1 + (dia1 + 1) / 2 + (dia2 + 1) / 2 + +// How to find diameter of undirected tree? +// Greedy Algorithm +// 1. Choose any node (a) +// 2. Find farthest node from a (b) --> BFS(levelorder) +// 3. Find farthest node from b and count no. of hops / levels +// diameter = no. of hops + +// Case 1: Your chosen node is already on diameter + +// Joining may not always contain diameter + +// max diameter = max{dia1, dia2, 1 + (dia1 + 1) / 2 + (dia2 + 1) / 2} + + diff --git a/LeetCode/Algorithms/Hard/LargestRectangleInHistogram.cpp b/LeetCode/Algorithms/Hard/LargestRectangleInHistogram.cpp new file mode 100644 index 0000000..7149fcf --- /dev/null +++ b/LeetCode/Algorithms/Hard/LargestRectangleInHistogram.cpp @@ -0,0 +1,42 @@ +class Solution { +public: + int largestRectangleArea(vector& heights) { + int maxArea = 0; + stack> stk; // (index, height) + int n = heights.size(); + for(int i=0; i heights[i]) { + auto temp = stk.top(); + stk.pop(); + + int index = temp.first; + int height = temp.second; + maxArea = max(maxArea, height * (i - index)); + start = index; + } + + stk.push({start, heights[i]}); + } + + // calculate the area of the remaining elements + // in stack, area to be calcualted by condering from + // length of height array + while(!stk.empty()) { + auto temp = stk.top(); + stk.pop(); + + int index = temp.first; + int height = temp.second; + + maxArea = max(maxArea, height * (n - index)); + } + return maxArea; + } +}; + +// Time Complexity - O(N) +// Space Complexity - O(N) \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/MaximumNumberOfTasksYouCanAssign.cpp b/LeetCode/Algorithms/Hard/MaximumNumberOfTasksYouCanAssign.cpp new file mode 100644 index 0000000..616b7fe --- /dev/null +++ b/LeetCode/Algorithms/Hard/MaximumNumberOfTasksYouCanAssign.cpp @@ -0,0 +1,156 @@ +class Solution { + public: + bool canAssign(int mid, vector &workers, vector &tasks, int pills, int strength) { + // it means that if mid = 2, we'll be taking the last two values in workers and putting them in multiset + multiset usable_workers(workers.end() - mid, workers.end()); + + // iterating from right to left + for(int i=mid-1; i>=0; i--) { + auto curr_workers = --usable_workers.end(); + + if(*curr_workers < tasks[i]) { + if(pills <= 0) return false; + + // Optimal Strategy: Assign weakest worker to get the current task done + auto weakest_worker = usable_workers.lower_bound(tasks[i] - strength); + + if(weakest_worker == usable_workers.end()) { + return false; // no one can be assigned the current job (even using pill) + } + + pills--; + usable_workers.erase(weakest_worker); + } else { + usable_workers.erase(curr_workers); + } + } + return true; + } + + int maxTaskAssign(vector& tasks, vector& workers, int pills, int strength) { + sort(workers.begin(), workers.end()); + sort(tasks.begin(), tasks.end()); + + int low = 0; + int high = min(tasks.size(), workers.size()); + int mid; + + int assigned = 0; + while(low <= high) { + mid = low + (high - low) / 2; + + if(canAssign(mid, workers, tasks, pills, strength)) { + assigned = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + + return assigned; + } +}; + +/* +Optimal Matchup Strategy + +Strategy - 1: Strong fights the weak + +Strategy - 2: Strong fights strong. (fair) -- choose this one + +Ques: What if you have G > R or G < R + +Note: Sort to search optimal matchups faster + +Goal: Get more wins for green + +Note: +Fights are only 1:1 +A person can only fight once + +1 7 6 +2 6 5 + +Sorting: + +1. Remove the person who is the strongest in the opposite team + + +I. +II. 2, (4, 5, 6) -> (N - M) workers ignore + +Sorted Asc: Strongest worker gets hardest job + 0 1 2 +T[]: [6, 7, 7] , Pill = 1, Strength = 3 +W[]: [2, 5, 6] + +Lower bound binary search: + + 0 1 2 3 4 5 6 7 +[1, 2, 2, 4, 4, 5, 7, 7] + +lb(2) = 1 +lb(6) = 6 + +// set shoudl not be used because the order is not unique + +multiset works well in this scenario since it preserves order +or +ordered_map + +Max tasks to be done <= Max workers + + 0 1 2 3 4 +T[] : [7, 6, 8, 7, 9] => T[]: [6, 7, 7, 8, 9] + <---- | ignore + 0 1 2 0 1 2 +W[] : [6, 2, 5] => W[]: [2, 5, 6] + +Strategy: Choose weakest M tasks + +If N < M then only N workers + + 0 1 +T[]: [6, 7] --> N + + 0 1 2 +W[]: [2, 5, 6] --> M +ignore| + +M > N + +Note: R->L is imp to match strongest W[i] with toughest task + +Max No of Tasks: Similar to Leetcode's First Bad version probelm + +Goal: Find max tasks which can be done +-> find the first bad version + +low = 0 +high = min(N, M) +mid = low + (high - low) / 2 + +if(canAssign(mid)) { + assigned = mid; + love = mid + 1; +} else { + love = mid - 1; +} + +pick strongest worker and weakest task, so that we have change of completing the task + +Time Complexity - + +W = min(N, M) + +logW * W * logW += W(logW)**2 + +Binary Search ---> Loop ---> lower bound on multiset + + +Total time Complexity - W(logW)^2 + NlogN + MlogM, tasks and workers array will also be sorted +Space Complexity - O(W), due to multiset / map being used + +W = min(N, M) +*/ \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/MaximumSumOf3NonOverlappingSubarrays.cpp b/LeetCode/Algorithms/Hard/MaximumSumOf3NonOverlappingSubarrays.cpp new file mode 100644 index 0000000..09dcad5 --- /dev/null +++ b/LeetCode/Algorithms/Hard/MaximumSumOf3NonOverlappingSubarrays.cpp @@ -0,0 +1,100 @@ +class Solution { +public: + vector prefix_sum; + int max_sum; + int mem[20001][3]; // [pos][count] + + int findMaxSum(vector &nums, int pos, int count, int &k) { + if(count == 3) return 0; // done case + if(pos > nums.size() - k) return 0; // not enough item case + if(mem[pos][count] != -1) return mem[pos][count]; // Repeating sub-problem + + // Don't start subarray here + int dont_start = findMaxSum(nums, pos + 1, count, k); + + // Start subarray here + int start_here = findMaxSum(nums, pos + k, count + 1, k) + + + prefix_sum[pos + k] - prefix_sum[pos]; + + return mem[pos][count] = max(dont_start, start_here); + } + + void findMaxSumPath(vector &nums, int pos, int count, int &k, vector &path) { + if(count == 3) return; + if(pos > nums.size() - k) return; + + // Don't start subarray here + int dont_start = findMaxSum(nums, pos + 1, count, k); // In O(1) time + + // Start subarray here + int start_here = findMaxSum(nums, pos + k, count + 1, k) // In O(1) time + + prefix_sum[pos + k] - prefix_sum[pos]; + + if(start_here >= dont_start) { + path.push_back(pos); + findMaxSumPath(nums, pos + k, count + 1, k, path); // Include pos + } else { + findMaxSumPath(nums, pos + 1, count, k, path); // Don't include pos + } + } + + vector maxSumOfThreeSubarrays(vector& nums, int k) { + int n = nums.size(); + memset(mem, -1, sizeof(mem)); // + + // Calculate Prefix-Sum + prefix_sum = vector(n + 1, 0); + for(int i=0; i path; + findMaxSumPath(nums, 0, 0, k, path); + + return path; + } +}; + +// Total no. of subproblems = N * 3 = 3N +// Time Complexity - O(N) +// Space Complexity - O(N) + +// Goal: +// 1. Find 3 non-overlapping Subarrays with max sum +// 2. Return list of start indices (lexicographically smallest in case of +// multiple answers) + +// N = 8 +// K = 2 +// No. of K size Subarrays = (N - K + 1) +// SAs = 7 + +// 0, 1, 2, 3, 4, 5, 6, 7 +// Given: [1, 2, 1, 2, 6, 7, 5, 1] +// [0, 1], [3, 4], [5, 6] sum = 23 -- [0, 3, 5] -> this one is lexicographically smaller +// [1, 2], [3, 4], [5, 6] sum = 23 -- [1, 3, 5] + +// result = [0, 3, 5] +// BF -> O(N ** 3) solution, N^2 > 10^8 + +// Recursive Sum, RS(l, r) = Prefix Sum(r) - Prefix Sum(l - 1) + +// Let's find 2 non overlapping Subarrays with maxSum + +// a[] = [2, 3, 1, 5, 2, 7] +// psum[] = [0, 2, 5, 6, 11, 13, 20] + +// 2 * k size = 2 * 2 = 4 + +// Total no. of subproblems = N * 3 = 3N +// Time Complexity - O(N) +// Space Complexity - O(N) + +// Memoization table + +// 1. Count size column +// 2. N size row \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/MedianOfTwoSortedArrays.cpp b/LeetCode/Algorithms/Hard/MedianOfTwoSortedArrays.cpp new file mode 100644 index 0000000..6af46dc --- /dev/null +++ b/LeetCode/Algorithms/Hard/MedianOfTwoSortedArrays.cpp @@ -0,0 +1,44 @@ +class Solution { + public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + vector A = nums1, B = nums2; + + int total = nums1.size() + nums2.size(); + int half = total / 2; + + if(B.size() < A.size()) { + swap(A, B); + } + + // log(min(n, m)) + int l = 0, r = A.size() - 1; + while(true) { + int i = (l + r) / 2; // for A + int j = half - i - 2; // B + + // A -> Aright + // B -> Bleft + + // A -> Aleft + // B -> Bright + int Aleft = (i >= 0) ? A[i] : INT_MIN; + int Aright = (i + 1) < A.size() ? A[i + 1] : INT_MAX; + int Bleft = (j >= 0) ? B[j] : INT_MIN; + int Bright = (j + 1) < B.size() ? B[j + 1] : INT_MAX; + + // parition is correct + if(Aleft <= Bright && Bleft <= Aright) { + // if odd + if(total % 2) { + return min(Aright, Bright); + } + // even + return (max(Aleft, Bleft) + min(Bright, Aright)) / 2; + } else if(Aleft > Bright) + r = i - 1; + } else if(Bleft > Bright) { + l = i + 1; + } + } + } +}; \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/MinimumIntervalToIncludeEachQuery.cpp b/LeetCode/Algorithms/Hard/MinimumIntervalToIncludeEachQuery.cpp new file mode 100644 index 0000000..6105ccf --- /dev/null +++ b/LeetCode/Algorithms/Hard/MinimumIntervalToIncludeEachQuery.cpp @@ -0,0 +1,59 @@ +#define pii pair +class Solution { + public: + struct myComp { + bool operator() (pair &a, pair &b) { + if(a.first == b.first) { + return a.second < b.second; + } + return a.first > b.first; + } + }; + + vector minInterval(vector>& intervals, vector& queries) { + sort(intervals.begin(), intervals.end(), [](vector &a, vector &b) { + return a[0] < b[0]; + }); + + vector sorted_query = queries; + sort(sorted_query.begin(), sorted_query.end()); + + // priority_queue, greater> pq; + priority_queue, myComp> pq; + + int i = 0; + unordered_map m; + for(auto &q: sorted_query) { + // adding all the intervals + while(i < intervals.size() && intervals[i][0] <= q) { + auto &temp = intervals[i]; + int l = temp[0]; + int r = temp[1]; + pq.push({(r - l + 1), r}); + i++; + } + + // removing invalid intervals from + // priority queue + while(!pq.empty() && pq.top().second < q) { + pq.pop(); + } + + if(!pq.empty()) { + m[q] = pq.top().first; + } else { + m[q] = -1; + } + } + + vector result; + for(auto &q: queries) { + result.push_back(m[q]); + } + return result; + } +}; + +// Time Complexity - O(nlogn + qlogq), +// q is length of queries +// n is the lenght of intervals \ No newline at end of file diff --git a/LeetCode/Algorithms/Hard/MinimumWindowSubstring.cpp b/LeetCode/Algorithms/Hard/MinimumWindowSubstring.cpp new file mode 100644 index 0000000..91599cb --- /dev/null +++ b/LeetCode/Algorithms/Hard/MinimumWindowSubstring.cpp @@ -0,0 +1,46 @@ +class Solution { + public: + string minWindow(string s, string t) { + if(t == "") return ""; + + unordered_map countT, window; + + for(auto c: t) { + countT[c]++; + } + + int have = 0, need = countT.size(); + int l = 0; + pair result; + int resLen = INT_MAX; + for(int r=0; r