diff --git a/leetcode_java/src/main/java/LeetCodeJava/Greedy/SplitArrayIntoConsecutiveSubsequences.java b/leetcode_java/src/main/java/LeetCodeJava/Greedy/SplitArrayIntoConsecutiveSubsequences.java index 25efcb7e..caeeb119 100644 --- a/leetcode_java/src/main/java/LeetCodeJava/Greedy/SplitArrayIntoConsecutiveSubsequences.java +++ b/leetcode_java/src/main/java/LeetCodeJava/Greedy/SplitArrayIntoConsecutiveSubsequences.java @@ -1,5 +1,9 @@ package LeetCodeJava.Greedy; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + /** * 659. Split Array into Consecutive Subsequences * Medium @@ -54,4 +58,239 @@ public class SplitArrayIntoConsecutiveSubsequences { // // } + // V1 + // IDEA : MAP + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/solutions/2447905/two-maps-clean/ + public boolean isPossible_1(int[] nums) { + Map notPlacedCount = new HashMap<>(); // entry = {num, count of unplaced nums} + Map sequenceEndCount = new HashMap<>(); // entry = {num, count of sequences ending at num} + + for (int num : nums) increaseCount(notPlacedCount, num); + + for (int num : nums) { + boolean alreadyContains = notPlacedCount.get(num) == 0; + boolean canAddToExisting = sequenceEndCount.getOrDefault(num - 1, 0) > 0; + boolean canAddNewSequence = notPlacedCount.getOrDefault(num + 1, 0) > 0 && notPlacedCount.getOrDefault(num + 2, 0) > 0; + + if (alreadyContains) + continue; + + if (canAddToExisting) { + decreaseCount(notPlacedCount, num); + decreaseCount(sequenceEndCount, num - 1); + increaseCount(sequenceEndCount, num); + } + + else if (canAddNewSequence) { + decreaseCount(notPlacedCount, num); + decreaseCount(notPlacedCount, num + 1); + decreaseCount(notPlacedCount, num + 2); + increaseCount(sequenceEndCount, num + 2); + } + + else + return false; + } + return true; + } + + private void increaseCount(Map countMap, int num) { + countMap.put(num, countMap.getOrDefault(num, 0) + 1); + } + + private void decreaseCount(Map countMap, int num) { + countMap.put(num, countMap.get(num) - 1); + } + + + // V 1_1 + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/solutions/2447452/java-greedy-just-a-few-lines-explained/ + public boolean isPossible_1_1(int[] nums) { + int[] count = new int[2003]; + int[] end = new int[2003]; + for (int n : nums){ + count[n+1000]++; + } + for (int i = 0; i <= 2002; i++){ + if (count[i] < 0){ // can't be less than 0, return false + return false; + }else if (i <= 2000){ + int cont = Math.min(count[i], i==0?0:end[i-1]); // extend the subsequences + count[i] -= cont; + end[i] += cont; + count[i+1] -= count[i]; // start some new subsequences and we must take those + count[i+2] -= count[i]; // take those + end[i+2] += count[i]; // update end to include the new subsequences. + } + } + return true; + } + + + // V2 + // IDEA : GREEDY + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/solutions/2447452/java-greedy-just-a-few-lines-explained/ + public boolean isPossible_2(int[] nums) { + // greedy algorithm + if (nums == null || nums.length < 3) + return false; + + // map to save the frequency of each number + Map freq = new HashMap<>(); + + // map to save that the number of subsequences that are + // ended with number i + Map tail = new HashMap<>(); + + // first pass, fill teh freq map + for (int i : nums) { + freq.put(i, freq.getOrDefault(i, 0) + 1); + } + + // second pass: + for (int i : nums) { + // there is no such number available + if (freq.get(i) == 0) { + continue; + } + // if there is some subsequence that is ended with i - 1: + // then we can put the number i in the subsequence + if (tail.get(i-1) != null && tail.get(i-1) > 0) { + // the number of sequence ended with i-1 decreases + tail.put(i-1, tail.get(i-1) - 1); + // the number of sequences ended with i increases + tail.put(i, tail.getOrDefault(i, 0) + 1); + // we used one number i, decrease the freq + freq.put(i, freq.get(i) - 1); + } else { + // there is no such subsequence that is ended with i-1 + // we build a new subsequence start with i, + // we then need i + 1 and i + 2 to make a valid subsequence + if (freq.get(i+1) != null && freq.get(i+1) > 0 && + freq.get(i+2) != null && freq.get(i+2) > 0) { + // if we have available i + 1 and i + 2 + // we now have one more subsequence ended with i+2 + tail.put(i+2, tail.getOrDefault(i+2, 0) + 1); + // decrease the frequency + freq.put(i, freq.get(i) - 1); + freq.put(i + 1, freq.get(i + 1) - 1); + freq.put(i + 2, freq.get(i + 2) - 1); + } else { + return false; + } + } + } + + return true; + } + + // V3 + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/solutions/844738/java-very-easy-explanation-through-a-story-time-o-n-space-o-n/ + public boolean isPossible_3(int[] nums) { + // This hashmap tells us about whether a number in num is available for a job or not + HashMap avaibilityMap = new HashMap<>(); + + // This hashmap tells a number (say x), if there is a job vacancy for them + HashMap wantMap = new HashMap<>(); + + // We store the count of every num in nums into avaibilityMap. Basically, a number's count is the avaibility of it. + for(int i : nums){ + avaibilityMap.put(i, avaibilityMap.getOrDefault(i,0)+1); + } + + // We iterate through each number in the nums array. Remember the story ? So, treat them like a person. + for(int i=0;i0){ + // Yes, someone is looking, so we decrease the avaibility count of that number + avaibilityMap.put(nums[i], avaibilityMap.getOrDefault(nums[i],0)-1); + + // we also decrease its count from the job vacancy space / wantMap + wantMap.put(nums[i], wantMap.getOrDefault(nums[i],0)-1); + + // Then as a goodwill, he/she will also create a job vacancy for (num[i]+1) in job vacancy space / wantMap, as we need consecutive numbers only + wantMap.put(nums[i]+1, wantMap.getOrDefault(nums[i]+1,0)+1); + } + + // Ooh, we are here means nums[i] was not able to find a job. + // so, nums[i] tries to start his/her own company by checking avaibility of his/her friends i.e. (nums[i]+1) and (nums[i]+2) + else if(avaibilityMap.getOrDefault(nums[i],0)>0 && avaibilityMap.getOrDefault(nums[i]+1,0)>0 && avaibilityMap.getOrDefault(nums[i]+2,0)>0){ + + // Yay! both 2 friends are available. Let's start a company. + // So we decrease the avaibility count of nums[i], (nums[i]+1) and (nums[i]+2) from the + // avaibilityMap + avaibilityMap.put(nums[i], avaibilityMap.getOrDefault(nums[i],0)-1); + avaibilityMap.put(nums[i]+1, avaibilityMap.getOrDefault(nums[i]+1,0)-1); + avaibilityMap.put(nums[i]+2, avaibilityMap.getOrDefault(nums[i]+2,0)-1); + + // Also, as a goodwill, we create a new job vacancy for (nums[i]+3), as we need consecutive numbers only + wantMap.put(nums[i]+3, wantMap.getOrDefault(nums[i]+3,0)+1); + } + + // Bad luck case, nums[i] not able to start his/her company, so just return false + else{ + return false; + } + } + + // All good till here so we return true + return true; + } + + // V4 + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/solutions/2448247/short-c-java-python-explained-solution-beginner-friendly-by-mr-coder/ + public boolean isPossible_4(int[] nums) { + Map availability = new HashMap<>(); + Map possibility = new HashMap<>(); + for(int num:nums){ + availability.put(num,availability.getOrDefault(num,0)+1); + } + for(int num:nums){ + if(availability.get(num)==0)continue; + if(possibility.getOrDefault(num,0)>0){ + possibility.put(num,possibility.getOrDefault(num,0)-1); + possibility.put(num+1,possibility.getOrDefault(num+1,0)+1); + } + else if(availability.getOrDefault(num+1,0)>0 && availability.getOrDefault(num+2,0)>0 ){ + possibility.put(num+3,possibility.getOrDefault(num+3,0)+1); + availability.put(num+1,availability.getOrDefault(num+1,0)-1); + availability.put(num+2,availability.getOrDefault(num+2,0)-1); + } + else{ + return false; + } + availability.put(num,availability.get(num)-1); + } + return true; + } + + // V5 + // IDEA: DP + GREEDY + PQ + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/submissions/1385341810/ + public boolean isPossible_5(int[] nums) { + Map>lastElements = new HashMap<>(); + for (int element: nums){ + int subseqCount = 0; + if (lastElements.containsKey(element-1)){ + subseqCount = lastElements.get(element-1).poll(); + if (lastElements.get(element-1).isEmpty()) lastElements.remove(element-1); + } + lastElements.putIfAbsent(element, new PriorityQueue<>()); + lastElements.get(element).add(subseqCount+1); + } + for (Map.Entry>entry: lastElements.entrySet()){ + while (!entry.getValue().isEmpty()){ + if (entry.getValue().poll()<3){ + return false; + } + } + } + return true; + } + } diff --git a/leetcode_java/src/main/java/dev/workspace3.java b/leetcode_java/src/main/java/dev/workspace3.java index c195f40a..1d594520 100644 --- a/leetcode_java/src/main/java/dev/workspace3.java +++ b/leetcode_java/src/main/java/dev/workspace3.java @@ -7611,4 +7611,98 @@ public int compare(int[] o1, int[] o2) { return res; } + // LC 659 + // https://leetcode.com/problems/split-array-into-consecutive-subsequences/ + // 7.51 pm - 8.10 pm + /** + * NOTE + * + * 1. nums that is sorted in non-decreasing order. + * + * conditions are true: + * + * - 1) Each subsequence is a consecutive increasing sequence (i.e. each integer is exactly one more than the previous integer). + * - 2) All subsequences have a length of 3 or more. + * + * exp 1 + * + * Input: nums = [1,2,3,3,4,5] + * Output: true + * Explanation: nums can be split into the following subsequences: + * [1,2,3,3,4,5] --> 1, 2, 3 + * x x x + * + * + * [1,2,3,3,4,5] --> 3, 4, 5 + * x x x + * + * exp 2 + * + * Input: nums = [1,2,3,3,4,4,5,5] + * Output: true + * Explanation: nums can be split into the following subsequences: + * [1,2,3,3,4,4,5,5] --> 1, 2, 3, 4, 5 + * [1,2,3,3,4,4,5,5] --> 3, 4, 5 + * + * + * [1,2] + * + * 3,4,5 + * 3,4,5 + * + * + * exp 3 + * + * nums = [1,2,3,4,4,5] + * + * 1,2,3,4 + * 4 + * + * exp 4 + * + * nums = [1,2,3,3,4,4,5,5] + * + * 1,2,3,4,5 + * 3,4,5 + * + * exp 5 + * + * nums = [1,2,3,3,3,4,4,4,5,5,5] + * + * 1,2,3,4,5 + * 3,4,5 + * 3,4,5 + */ +// public boolean isPossible(int[] nums) { +// +// if (nums.length == 1){ +// return true; +// } +// +// if (nums.length < 3){ +// return false; +// } +// +// List> cache = new ArrayList<>(); +// +// //cache.add(new ArrayList<>()); +// // brute force +// for(int x : nums){ +// int size = cache.size(); +// if(size==0){ +// List tmp = new ArrayList<>(); +// tmp.add(x); +// cache.add(tmp); +// continue; +// } if (!cache.get(size).contains(x)){ +// List tmp = cache.get(size); +// tmp.add(x); +// cache.add(tmp); +// continue; +// } +// +// return false; +// } + + }