diff --git a/resources/sliding-window.md b/resources/sliding-window.md index 5605ed9..d9592ea 100644 --- a/resources/sliding-window.md +++ b/resources/sliding-window.md @@ -8,6 +8,8 @@ - [Longest Substring With K Unique Characters / Variable Size Sliding Window](/src/com/problems/slidingwindow/LargestSubstringWithKUniqueCharacters.java) - [Longest Substring With Without Repeating Characters / Variable Size Sliding Window](/src/com/problems/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java) - [Fruit Into Baskets / Pick Toys / An Interesting Sliding Window Problem](/src/com/problems/slidingwindow/FruitIntoBaskets.java) +- [Minimum Number of Flips to Make the Binary String Alternating](/src/com/problems/slidingwindow/MinimumNumberOfFlipsToMakeTheBinaryStringAlternating.java) +- [Maximum Number of Vowels in a Substring of Given Length](/src/com/problems/slidingwindow/MaximumNumberOfVowelsInASubstringOfGivenLength.java) - [Minimum Window Substring / Variable Size Sliding Window](/src/com/problems/slidingwindow/MinimumWindowSubstring.java) - [Minimum Window Subsequence](/src/com/problems/slidingwindow/MinimumWindowSubsequence.java) - [Maximum Sum of Distinct Subarrays With Length K](/src/com/problems/slidingwindow/MaximumSumOfDistinctSubarraysWithLengthK.java) @@ -16,6 +18,7 @@ - [Number of Substrings Containing All Three Characters](/src/com/problems/slidingwindow/NumberOfSubstringsContainingAllThreeCharacters.java) - [Max Consecutive Ones III](/src/com/problems/slidingwindow/MaxConsecutiveOnes3.java) - [Longest Repeating Character Replacement](/src/com/problems/slidingwindow/LongestRepeatingCharacterReplacement.java) +- [Permutation in String](/src/com/problems/slidingwindow/PermutationInString.java) - [Binary Subarrays With Sum](/src/com/problems/slidingwindow/BinarySubarraysWithSum.java) - [Count Number of Nice Subarrays](/src/com/problems/slidingwindow/CountNumberOfNiceSubarrays.java) - [Maximum Points You Can Obtain from Cards](/src/com/problems/slidingwindow/MaximumPointsYouCanObtainFromCards.java) diff --git a/src/com/problems/slidingwindow/FrequencyOfTheMostFrequentElement.java b/src/com/problems/slidingwindow/FrequencyOfTheMostFrequentElement.java index 1538767..653f807 100644 --- a/src/com/problems/slidingwindow/FrequencyOfTheMostFrequentElement.java +++ b/src/com/problems/slidingwindow/FrequencyOfTheMostFrequentElement.java @@ -7,19 +7,23 @@ * https://leetcode.com/problems/frequency-of-the-most-frequent-element/description/ * * Solution link : - * Neetcode : https://www.youtube.com/watch?v=vgBrQ0NM5vE - * Aryan Mittal : https://www.youtube.com/watch?v=e0AFPpKcjw0 + * Neetcode + * https://www.youtube.com/watch?v=vgBrQ0NM5vE + * Aryan Mittal : + * https://www.youtube.com/watch?v=e0AFPpKcjw0 * * https://takeuforward.org/arrays/find-the-highest-lowest-frequency-element/ + * https://neetcode.io/solutions/frequency-of-the-most-frequent-element */ + public class FrequencyOfTheMostFrequentElement { public static void main(String[] args) { type1(); } - // using the sliding window approach - // time complexity O(nlog(n) + 2n) - // the intuition is kind of greedy + // todo using the sliding window approach + // time complexity O(nlog(n) + 2n) + // the intuition is kind of greedy // lets say if the nums array is [1,1,1,3,3,5] // and we are operating on 5 currently, and we need to choose other // numbers to convert them into 5, if we change 1 into 5 it will take 4 unit @@ -30,7 +34,7 @@ public static void main(String[] args) { // let's say we are on j th element, we will assume that the left side numbers // will be converted into this, if the left point is on i // so the total range is (j-i+1) - // and our criteria will be nums[j]*(j-i+1) - range-sum(j-i+1) <= k + // and our criteria will be (nums[j] * (j - i + 1)) - range-sum(j - i + 1) <= k // if it is less than equal to k then it is possible to convert all the numbers in that range // else we have to increment left till it does not fit into the criteria // we will also need to carry the range sum @@ -44,20 +48,23 @@ private static void type1() { public static int maxFrequency1(int[] nums, int k) { // we need to sort at first Arrays.sort(nums); + int n = nums.length; + long sum = 0; int max = 0; - int n = nums.length, left = 0, right = 0; - while (right < n) { + // we will go from 0 to n and choose current number to be target number for all the left side numbers + for (int right = 0, left = 0; right < n; right++) { // adding the current element in the range sum long num = nums[right]; - sum += num; + sum += num; // updating the sum + // if num * range - range sum > k then we can not transform all the numbers to the current num + // so we will decrease from left and shrink the window while (num * (right - left + 1) - sum > k) { // decrementing the left most element from that range sum -= nums[left++]; } // checking the max frequency max = Math.max(max, right - left + 1); - right++; } return max; } diff --git a/src/com/problems/slidingwindow/FruitIntoBaskets.java b/src/com/problems/slidingwindow/FruitIntoBaskets.java index c51f097..78f3399 100644 --- a/src/com/problems/slidingwindow/FruitIntoBaskets.java +++ b/src/com/problems/slidingwindow/FruitIntoBaskets.java @@ -5,12 +5,16 @@ /* * Problem link: - * https://leetcode.com/problems/fruit-into-baskets/ - * https://www.codingninjas.com/studio/problems/fruits-and-baskets_985356 + * https://leetcode.com/problems/fruit-into-baskets/description/ + * https://www.naukri.com/code360/problems/fruits-and-baskets_985356 * * Solution: - * Aditya Verma : https://www.youtube.com/watch?v=seOKHXB_w74&list=PL_z_8CaSLPWeM8BDJmIYDaoQ5zuwyxnfj&index=12 - * + * Aditya Verma : + * https://www.youtube.com/watch?v=seOKHXB_w74&list=PL_z_8CaSLPWeM8BDJmIYDaoQ5zuwyxnfj&index=12 + * Neetcode: + * https://www.youtube.com/watch?v=yYtaV0G3mWQ + * + * https://neetcode.io/solutions/fruit-into-baskets */ public class FruitIntoBaskets { @@ -24,17 +28,15 @@ public class FruitIntoBaskets { // Starting from any tree of your choice, you must pick exactly one fruit from // every tree (including the start tree) while moving to the right. The picked // fruits must fit in one of your baskets. - // Once you reach a tree with fruit that cannot fit in your baskets, you must - // stop. - // Given the integer array fruits, return the maximum number of fruits you can - // pick. + // Once you reach a tree with fruit that cannot fit in your baskets, you must stop. + // Given the integer array fruits, return the maximum number of fruits you can pick. public static void main(String[] args) { type1(); + // sliding window type2(); type3(); + // we will just use prev and prev2 variables type4(); - type5(); - type6(); } // TODO best possible solution, same as type5 just it is less complicated to understand @@ -49,177 +51,142 @@ public static void main(String[] args) { // if there is a new type of fruit then we will set that count to 1 // because the current fruit will become my new prev fruit // we will change the pointers accordingly - private static void type6() { + private static void type4() { int[] fruits = {6, 2, 1, 1, 3, 6, 6}; - int max = 0; - int curMax = 0; - int prev = -1; - int prev2 = -1; - int prevContinuousCount = 0; - for (int fruit : fruits) { - if (fruit == prev) { - curMax++; - prevContinuousCount++; - } else { - if (fruit == prev2) curMax++; - else curMax = prevContinuousCount + 1; - prevContinuousCount = 1; - prev2 = prev; - prev = fruit; - } - max = Math.max(max, curMax); - } + int max = totalFruit5(fruits); System.out.println(max); } - // TODO best possible solution in leetcode - private static void type5() { - int[] fruits = {6, 2, 1, 1, 3, 6, 6}; + private static int totalFruit5(int[] fruits) { int max = 0; - int curMax = 0; - int prev = -1; - int prev2 = -1; - int prevCount = 0; + // we will use 2 variables prev and prev2 for storing the fruits which are + int prev = -1, prev2 = -1; + // count of the current series with prev and prev2 type of fruits + int count = 0; + // if the current fruit is not the previous fruit then we have 2 cases either it is prev2 or different fruit + // if it is prev2 then the series should continue + // but if it is a different fruit then we need the count of prevFruit [which we can find using a loop] + int prevFruitContinuousCount = 0; for (int fruit : fruits) { - if (fruit == prev || fruit == prev2) { - curMax++; - } else { - max = Math.max(max, curMax); - curMax = prevCount + 1; - } if (fruit == prev) { - prevCount++; + count++; + prevFruitContinuousCount++; } else { - prevCount = 1; + if (fruit == prev2) { + count++; // continue the fruit series + } else { + count = prevFruitContinuousCount + 1; // create a new series with prevFruitContinuousCount and the current fruit + } + // we will now update all the variables + prevFruitContinuousCount = 1; prev2 = prev; prev = fruit; } + max = Math.max(max, count); } - max = Math.max(max, curMax); - System.out.println(max); + return max; } - // TODO best possible solution I came up with - private static void type4() { + + // same as previous but here we will use the array as freq map + private static void type3() { int[] fruits = {6, 2, 1, 1, 3, 6, 6}; - int k = 2; + int max = totalFruit3(fruits); + System.out.println(max); + } + + private static int totalFruit3(int[] fruits) { int n = fruits.length; - int left = 0, max = 0; + int max = 0; int types = 0; - int[] freq = new int[100001]; - for (int right = 0; right < n; right++) { + // creating the frequency array + int maxFruit = -1; + for (int fruit : fruits) { + maxFruit = Math.max(fruit, maxFruit); + } + int[] freq = new int[maxFruit + 1]; + // we will go through the array and add the fruits one by one + for (int left = 0, right = 0; right < n; right++) { int fruit = fruits[right]; + // it is a new fruit if (freq[fruit] == 0) types++; freq[fruit]++; - while (left < n && types > k) { - int fruitToBeRemoved = fruits[left++]; - freq[fruitToBeRemoved]--; - if (freq[fruitToBeRemoved] == 0) types--; + // if type is greater than 2 then we will shrink the window + while (left < right && types > 2) { + int leftFruit = fruits[left++]; + // it is the last fruit, so we will decrease the type + if (freq[leftFruit] == 1) types--; + freq[leftFruit]--; } - max = Math.max(max, right - left + 1); + max = Math.max((right - left + 1), max); } - System.out.println(max); + return max; } - private static void type3() { - int[] fruits = {6, 2, 1, 1, 3, 6, 6}; - int k = 2; - int n = fruits.length; - int left = 0, max = 0; - int types = 0; - Map map = new HashMap<>(); - for (int right = 0; right < n; right++) { - int fruit = fruits[right]; - if (!map.containsKey(fruit) || map.get(fruit) == 0) { - map.put(fruit, 1); - types++; - } else map.put(fruit, map.get(fruit) + 1); - while (left < n && types > k) { - int fruitToBeRemoved = fruits[left++]; - int fruitToBeRemovedCount = map.get(fruitToBeRemoved); - map.put(fruitToBeRemoved, fruitToBeRemovedCount - 1); - if (fruitToBeRemovedCount == 1) types--; - } - max = Math.max(max, right - left + 1); - } + + // todo optimized approach using sliding window + // this is max toys problem which is same as the max fruits problem + private static void type2() { + int[] fruits = {1, 2, 3, 2, 2}; + int max = totalFruit2(fruits); + System.out.println(max); + + char[] toys = {'a', 'b', 'a', 'a', 'c', 'c', 'a', 'b'}; + max = totalToys1(toys); System.out.println(max); } - // sliding window - private static void type2() { - int[] fruits = { 1, 2, 3, 2, 2 }; - int k = 2; + private static int totalFruit2(int[] fruits) { int n = fruits.length; - int left = 0, right = 0, max = 0; + int max = 0; Map map = new HashMap<>(); int types = 0; - int fruitsSize = 0; - while (right < n && types < k) { - int fruit = fruits[right++]; - if (!map.containsKey(fruit)) { - map.put(fruit, 1); - types++; - } else map.put(fruit, map.get(fruit) + 1); - } -// if (right == n) return n; - max = right; - while (right < n) { - int fruit = fruits[right++]; - if (!map.containsKey(fruit)) { - map.put(fruit, 0); - } - map.put(fruit, map.get(fruit) + 1); - while (map.keySet().size() != k) { - int prev = fruits[left]; - int c = map.get(prev); - if (c == 1) { - map.remove(prev); - } else { - map.put(prev, c - 1); - } - left++; + // we will go through the array and add the fruits one by one + for (int left = 0, right = 0; right < n; right++) { + int fruit = fruits[right]; + // it is a new fruit + if (map.getOrDefault(fruit, 0) == 0) types++; + map.put(fruit, map.getOrDefault(fruit, 0) + 1); + // if type is greater than 2 then we will shrink the window + while (left < right && types > 2) { + int leftFruit = fruits[left++]; + // it is the last fruit, so we will decrease the type + if (map.get(leftFruit) == 1) types--; + map.put(leftFruit, map.get(fruit) - 1); } - max = Math.max((right - left), max); + max = Math.max((right - left + 1), max); } - System.out.println(max); + return max; } - // sliding window - private static void type1() { - char[] toys = { 'a', 'b', 'a', 'a', 'c', 'c', 'a', 'b' }; - int k = 2; + private static int totalToys1(char[] toys) { int n = toys.length; - int left = 0, right = 0, max = 0; + int max = 0; Map map = new HashMap<>(); - while (right < n && map.keySet().size() < k) { - char toy = toys[right++]; - if (!map.containsKey(toy)) { - map.put(toy, 0); - } - map.put(toy, map.get(toy) + 1); - } - if (right == n) - return; - max = right; - while (right < n) { - char toy = toys[right++]; - if (!map.containsKey(toy)) { - map.put(toy, 0); - } - map.put(toy, map.get(toy) + 1); - while (map.keySet().size() != k) { - char prev = toys[left]; - int c = map.get(prev); - if (c == 1) { - map.remove(prev); - } else { - map.put(prev, c - 1); - } - left++; + int types = 0; + for (int left = 0, right = 0; right < n; right++) { + char toy = toys[right]; + // it is a new toy + if (map.getOrDefault(toy, 0) == 0) types++; + map.put(toy, map.getOrDefault(toy, 0) + 1); + // if type is greater than 2 then we will shrink the window + while (left < right && types > 2) { + char leftToy = toys[left++]; + // it is the last toy, so we will decrease the type + if (map.get(leftToy) == 1) types--; + map.put(leftToy, map.get(toy) - 1); } - max = Math.max((right - left), max); + max = Math.max((right - left + 1), max); } - System.out.println(max); + return max; } + + // brute force approach + private static void type1() { + + } + + + } diff --git a/src/com/problems/slidingwindow/LongestRepeatingCharacterReplacement.java b/src/com/problems/slidingwindow/LongestRepeatingCharacterReplacement.java index 7237ac0..3df3b9e 100644 --- a/src/com/problems/slidingwindow/LongestRepeatingCharacterReplacement.java +++ b/src/com/problems/slidingwindow/LongestRepeatingCharacterReplacement.java @@ -1,5 +1,6 @@ package com.problems.slidingwindow; +import java.util.Arrays; /* * * problem links : @@ -9,20 +10,48 @@ * * Solution link : * https://www.youtube.com/watch?v=_eNhaDCr6P0 + * https://www.youtube.com/watch?v=gqXU1UyA8pk * - * - * - * Tags : - * Sliding-Window + * https://neetcode.io/solutions/longest-repeating-character-replacement * */ + + +// Tags: Array, Sliding window public class LongestRepeatingCharacterReplacement { + + + /* + * You are given a string s and an integer k. You can choose any character of the string and + * change it to any other uppercase English character. You can perform this operation at most k times. + * + * Return the length of the longest substring containing the same letter you can get after performing the above operations. + * + * */ + + /* + * You should aim for a solution with O(n) time and O(m) space, + * where n is the length of the given string and m is the number of unique characters in the string. + * Which characters would you replace in a string to make all its characters unique? Can you think with respect to the frequency of the characters? + * It is always optimal to replace characters with the most frequent character in the string. Why? + * Because using the most frequent character minimizes the number of replacements required to make all characters in the string identical. + * How can you find the number of replacements now? + * + * The number of replacements is equal to the difference between the length of the string and the frequency of the most frequent character in the string. + * A brute force solution would be to consider all substrings, use a hash map for frequency counting, + * and return the maximum length of the substring that has at most k replacements. This would be an O(n^2) solution. Can you think of a better way? + * + * We can use the sliding window approach. The window size will be dynamic, and we will shrink the window when the number of replacements exceeds k. + * The result will be the maximum window size observed at each iteration. + * */ public static void main(String[] args) { type1(); type2(); type3(); } - // todo check again + // todo optimized approach using the sliding window + // see the solution first then come up with the intuition + // lets take the series of A B C D E E E A B C F F A B F F F and k is 3 private static void type3() { String s = "BAAAB"; int k = 2; @@ -32,35 +61,108 @@ private static void type3() { } private static int characterReplacement3(String s, int k) { - int max = 0; char[] arr = s.toCharArray(); + int n = arr.length; int[] freq = new int[26]; - + // we will use 2 pointer int left = 0, right = 0; - int n = arr.length; - int curentMaxFrequency = 0; + int max = 0; + int currMaxFreq = 0; while (right < n) { int rightCh = arr[right++] - 'A'; freq[rightCh]++; - // Get the largest count of a single, unique character in the current window... - curentMaxFrequency = Math.max(curentMaxFrequency, freq[rightCh]); + // update a maximum frequency of any character in the current window + currMaxFreq = Math.max(currMaxFreq, freq[rightCh]); + // todo we will choose the character with the most freq to be the target character // We are allowed to have at most k replacements in the window... // So, if max character frequency + distance between beg and end is greater than k... - // this means we have considered changing more than k characters. So time to shrink window... + // this means we have considered changing more than k characters. + // So we will shrink the window from the left side // Then there are more characters in the window than we can replace, and we need to shrink the window... - while ((right - left + 1) - curentMaxFrequency > k) { - int leftCh = arr[left++] - 'A'; - freq[leftCh]--; + // todo let's say the target characters are on the left side so if we shrink then those will be deleted still we don't need to bother, + // Note: we don't need to update maxFreq here even though we are shrinking the window + // because we do not need to decrease the max frequency as the result will be impacted by only a higher max freq + // even if we are computing it wrong at some point still we don't need to decrease + // later if we find any higher maxFreq then only it will impact the answer + while ((right - left + 1) - currMaxFreq > k) { + int leftCh = arr[left++] - 'A'; // getting the left char and also incrementing left ptr + freq[leftCh]--; // decreasing the character frequency } - // Get the maximum length of repeating character.. - max = Math.max(max, right - left + 1); + // Get the length of repeating character.. + max = Math.max(max, (right - left + 1)); } return max; } + + // like the previous, but here we will not calculate everytime + // once (right - left + 1) - maxF > k then we will shrink the left side and calculate maxF again private static void type2() { + String s = "BAAAB"; + int k = 2; + int max = characterReplacement2(s, k); + System.out.println(max); + } + + private static int characterReplacement2(String s, int k) { + char[] arr = s.toCharArray(); + int n = arr.length; + int[] freq = new int[26]; + int max = 0; + int maxF = 0; + for (int right = 0, left = 0; right < n; right++) { + int pos = arr[right] - 'A'; + freq[pos]++; + maxF = Math.max(maxF, freq[pos]); + // max character changes is more than k, so we will try to shrink the left side + while ((right - left + 1) - maxF > k) { + int leftPos = arr[left++] - 'A'; + freq[leftPos]--; + // and again calculating the maximum freq + maxF = max(freq); + } + // Get the length of repeating character.. + max = Math.max(max, (right - left + 1)); + } + return max; } + private static int max(int[] freq) { + int maxF = 0; + for (int f : freq) { + maxF = Math.max(maxF, f); + } + return maxF; + } + + + // for every index we will try to expand it the characters can be changed + // into the character which has the max freq in that range private static void type1() { + String s = "BAAAB"; + int k = 2; + int max = characterReplacement1(s, k); + System.out.println(max); + + } + + private static int characterReplacement1(String s, int k) { + char[] arr = s.toCharArray(); + int n = arr.length; + int[] freq = new int[26]; + int max = 0; + for (int i = 0; i < n; i++) { + int maxF = 0; + for (int j = i; j < n; j++) { + int pos = arr[j] - 'A'; + freq[pos]++; + maxF = Math.max(maxF, freq[pos]); + // max character changes is more than k + if ((j - i + 1) - maxF > k) break; + max = Math.max(max, (j - i + 1)); + } + Arrays.fill(freq, 0); + } + return max; } } diff --git a/src/com/problems/slidingwindow/MaximumNumberOfVowelsInASubstringOfGivenLength.java b/src/com/problems/slidingwindow/MaximumNumberOfVowelsInASubstringOfGivenLength.java new file mode 100644 index 0000000..2812a00 --- /dev/null +++ b/src/com/problems/slidingwindow/MaximumNumberOfVowelsInASubstringOfGivenLength.java @@ -0,0 +1,58 @@ +package com.problems.slidingwindow; +/* + * + * problem links : + * https://leetcode.com/problems/maximum-number-of-vowels-in-a-substring-of-given-length/description/ + * + * Solution link : + * https://www.youtube.com/watch?v=kEfPSzgL-Ss + * + * https://neetcode.io/solutions/maximum-number-of-vowels-in-a-substring-of-given-length + * */ + + +// Tags: Array, Sliding window +public class MaximumNumberOfVowelsInASubstringOfGivenLength { + public static void main(String[] args) { + type1(); + type2(); + } + + // sliding window approach + private static void type2() { + String s = "abciiidef"; + int k = 3; + int ans = maxVowels2(s, k); + System.out.println(ans); + } + + public static int maxVowels2(String s, int k) { + char[] arr = s.toCharArray(); + int n = arr.length; + int v = 0; + // for the first window + for (int i = 0; i < k; i++) { + if (isVowel(arr[i])) v++; + } + int max = v; + // for the remaining window + for (int i = k; i < n; i++) { + if (isVowel(arr[i])) v++; + if (isVowel(arr[i - k])) v--; + max = Math.max(max, v); + } + return max; + } + + static boolean isVowel(char ch) { + return switch (ch) { + case 'a', 'e', 'i', 'o', 'u' -> true; + default -> false; + }; + } + + // brute force + private static void type1() { + + } +} diff --git a/src/com/problems/slidingwindow/MinimumNumberOfFlipsToMakeTheBinaryStringAlternating.java b/src/com/problems/slidingwindow/MinimumNumberOfFlipsToMakeTheBinaryStringAlternating.java new file mode 100644 index 0000000..d2182f6 --- /dev/null +++ b/src/com/problems/slidingwindow/MinimumNumberOfFlipsToMakeTheBinaryStringAlternating.java @@ -0,0 +1,172 @@ +package com.problems.slidingwindow; +/* + * + * problem links : + * https://leetcode.com/problems/minimum-number-of-flips-to-make-the-binary-string-alternating/description/ + * + * Solution link : + * https://www.youtube.com/watch?v=MOeuK6gaC2A + * + * https://neetcode.io/solutions/minimum-number-of-flips-to-make-the-binary-string-alternating + * */ + + +// Tags: Array, Sliding window +public class MinimumNumberOfFlipsToMakeTheBinaryStringAlternating { + public static void main(String[] args) { + type1(); + type2(); // todo check this one first this is the origin type3 and type4 are same as this + type3(); + type4(); + } + + // todo exactly like the prev with minor changes + private static void type4() { + String s = "111000"; + int ans = minFlips4(s); + System.out.println(ans); + } + + private static int minFlips4(String s) { + char[] arr = s.toCharArray(); + int n = arr.length; + int flips = 0; + // first we will create the window for the first n size + for (int i = 0; i < n; i++) { + boolean isEven = (i % 2) == 0; + char bit = arr[i]; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + } + int min = Math.min(flips, n - flips); + // if n is even then we do not need to do anything else + if ((n % 2) == 0 || min == 0) return min; + // at this point n is odd + for (int i = 0; i < n; i++) { + char bit = arr[i]; + boolean isEven = ((i + n) % 2) == 0; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + else if ((!isEven && bit == '1') || (isEven && bit == '0')) { + flips--; + } + // updating the min variable + int currentWindowMin = Math.min(flips, n - flips); + min = Math.min(min, currentWindowMin); + } + return min; + } + + // similar intuition like the previous with more optimization + // todo exactly like the previous but here we are assuming that the string is concatenated + // we will not attach the strings here we will do it by manipulating the indices + // if we see the 2nd sliding window loop it started from n and went till 2n + // left char is arr[i-n] and current char is arr[i] + // but both are similar characters actually, so we are actually calculating for same char + // and the main thing is if the indices are even or odd + // if n is even then if 'i' is even then i-n and i+n are even same for if 'i' is odd + // if n is odd then if 'i' is even then i-n and i+n will be odd same for if 'i' is odd, it will toggle + // so if n is even we do not need to do anything else + // if n is odd then we need to compute in the 2nd loop + private static void type3() { + String s = "111000"; + int ans = minFlips3(s); + System.out.println(ans); + } + + private static int minFlips3(String s) { + char[] arr = s.toCharArray(); + int n = arr.length; + int flips = 0; + // first we will create the window for the first n size + for (int i = 0; i < n; i++) { + boolean isEven = (i % 2) == 0; + char bit = arr[i]; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + } + int min = Math.min(flips, n - flips); + // if n is even then we do not need to do anything else + if ((n / 2) == 0 || min == 0) return min; + // now we will calculate for the remaining window + for (int i = 0; i < n; i++) { + // calculating for the current bit + boolean isEven = ((i + n) % 2) == 0; + char bit = arr[i]; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + + // calculating for the left most bit + boolean isLeftEven = (i % 2) == 0; + char leftBit = arr[i]; + if ((isLeftEven && leftBit == '1') || (!isLeftEven && leftBit == '0')) + flips--; + // updating the min variable + int currentWindowMin = Math.min(flips, n - flips); + min = Math.min(min, currentWindowMin); + } + return min; + } + + // todo optimized approach using the sliding window, discuss it in the interview + + // there is a trick for the first type which is + // if we append the same string in the string, + // and then we will create 'n' sliding window it will make the string + // as if some characters of string added in the last + // if the string is abcde then ss => abcdeabcde + // if we take a n size window then it will be ab-cdeab-cde + // the inner cdeab is same as removing then appending 2 characters at the end of the string + + // let's say we are assuming even index for 1 and odd index for 0, + // so we will keep a counter for required flips + // if in even index there is no 1 then we will increment the counter + // similarly for the 0's if that is not on odd index, + // but we could also assume that even index for 0 and odd index for 1 + // that also makes the string alternating + // we do not need to create a separate counter for that as the value will be (n-counter) + // minimum flips required will be min(counter, n-counter) + private static void type2() { + String s = "111000"; + int ans = minFlips2(s); + System.out.println(ans); + } + + + public static int minFlips2(String s) { + s = s + s; + char[] arr = s.toCharArray(); + int n2 = arr.length; + int n1 = n2 / 2; + int flips = 0; + // first we will create the window for the first n size + for (int i = 0; i < n1; i++) { + boolean isEven = (i % 2) == 0; + char bit = arr[i]; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + } + int min = Math.min(flips, n1 - flips); + if (min == 0) return 0; + for (int i = n1; i < n2; i++) { + // calculating for the current bit + boolean isEven = (i % 2) == 0; + char bit = arr[i]; + if ((isEven && bit == '1') || (!isEven && bit == '0')) + flips++; + // calculating for the left most bit + boolean isLeftEven = ((i - n1) % 2) == 0; + char leftBit = arr[i - n1]; + if ((isLeftEven && leftBit == '1') || (!isLeftEven && leftBit == '0')) + flips--; + // updating the min variable + int currentWindowMin = Math.min(flips, n1 - flips); + min = Math.min(min, currentWindowMin); + } + return min; + } + + // brute force approach + private static void type1() { + } +} diff --git a/src/com/problems/slidingwindow/PermutationInString.java b/src/com/problems/slidingwindow/PermutationInString.java new file mode 100644 index 0000000..c6189d5 --- /dev/null +++ b/src/com/problems/slidingwindow/PermutationInString.java @@ -0,0 +1,80 @@ +package com.problems.slidingwindow; + +/* + * + * problem links : + * https://leetcode.com/problems/permutation-in-string/description/ + * https://neetcode.io/problems/permutation-string + * + * Solution link : + * https://www.youtube.com/watch?v=UbyhOgBN834 + * + * https://neetcode.io/solutions/permutation-in-string + * */ +public class PermutationInString { + public static void main(String[] args) { + type1(); + type2(); + } + + // optimized approach using the sliding window + // now we have added all the characters of string1 to the freq array + // we will decrement the characters of the string 2 from the freq array + // so at any point if the freq array is all 0 then we will return 0 + private static void type2() { + String s1 = "ab", s2 = "eidbaooo"; + boolean ans = checkInclusion2(s1, s2); + System.out.println(ans); + } + + public static boolean checkInclusion2(String s1, String s2) { + char[] arr1 = s1.toCharArray(); + char[] arr2 = s2.toCharArray(); + int n1 = arr1.length, n2 = arr2.length; + if (n1 > n2) return false; + int[] freq = new int[26]; + // now we have added all the characters of string1 to the freq array + for (char ch : arr1) { + freq[ch - 'a']++; + } + // now we will use sliding window for all the n1 size window in the str2, + // but first we will start for the first window + for (int i = 0; i < n1; i++) { + int pos = arr2[i] - 'a'; + freq[pos]--; + } + if (isAllZero(freq)) return true; + // for the remaining window + for (int i = n1; i < n2; i++) { + // computing for the current char + int pos = arr2[i] - 'a'; + freq[pos]--; + // computing for the left char + int leftPos = arr2[i - n1] - 'a'; + freq[leftPos]++; + // now we have calculated for the current window + // if all 0 then we will return true + if (isAllZero(freq)) return true; + } + return false; + } + + private static boolean isAllZero(int[] freq) { + System.out.println(concat(freq)); + for (int f : freq) { + if (f != 0) return false; + } + return true; + } + + private static String concat(int[] freq) { + StringBuilder sb = new StringBuilder(); + for (int f : freq) sb.append(f).append(" "); + return sb.toString(); + } + + // brute force approach + private static void type1() { + + } +}