Sunday, February 25, 2024

[LeetCode] Maximum Number of Vowels in a Substring of Given Length

Problem: Given a string s and an integer k, return the maximum number of vowel letters in any substring of s with length k.

Vowel letters in English are 'a', 'e', 'i', 'o', and 'u'.

Example:

Input: s = "abciiidef", k = 3
Output: 3
Explanation: The substring "iii" contains 3 vowel letters.
Input: s = "aeiou", k = 2
Output: 2
Explanation: Any substring of length 2 contains 2 vowels.
Input: s = "leetcode", k = 3
Output: 2
Explanation: "lee", "eet" and "ode" contain 2 vowels.


Approach: We can take all the substring of size k of given string s and can check the number of vowels in those substrings and then we can return the maximum of all those counts but that will be time consuming task. 

We can use sliding window approach here to solve this problem. Obviously the size of the window here will be k. First we take the first k characters of s and count the number of vowels in it. Now we slide this k sized window one by one. The vowel count of the sliding window will be modified as follows:

curr_vowel_count =  curr_vowel_count - IsVowel(s[start]) + IsVowel(s[curr_index])

Here the curr_index is the end of the window and start is obviously the start of the window.


Implementation in C#:

        public int MaxVowels(string s, int k)

    {
        int length = s?.Length ?? 0;
        if (length < k)
        {
            return 0;
        }
        int currVowelCount = 0, i = 0;
        for (; i < k; ++i)
        {
            if (this.IsVowel(s[i]))
            {
                ++currVowelCount;
            }
        }
        int start = 0, maxVowelCount = currVowelCount;
        for (; i < length; ++i)
        {
            if (this.IsVowel(s[start++]))
            {
                --currVowelCount;
            }
            if (this.IsVowel(s[i]))
            {
                ++currVowelCount;
                maxVowelCount = maxVowelCount < currVowelCount ?
                                currVowelCount :
                                maxVowelCount;
            }
        }
        return maxVowelCount;
    }

    private bool IsVowel(char ch)
    {
        return ch == 'a' ||
               ch == 'e' ||
               ch == 'i' ||
               ch == 'o' ||
               ch == 'u';
    }

Complexity: O(n)

Friday, February 23, 2024

[LeetCode] Maximum Average Subarray I

Problem: You are given an integer array nums consisting of n elements, and an integer k.

Find a contiguous subarray whose length is equal to k that has the maximum average value and return this value. Any answer with a calculation error less than 10^-5 will be accepted.

Example:

Input: nums = [1,12,-5,-6,50,3], k = 4
Output: 12.75000
Explanation: Maximum average is (12 - 5 - 6 + 50) / 4 = 51 / 4 = 12.75
Input: nums = [5], k = 1
Output: 5.00000

Constraints:

  • n == nums.length
  • 1 <= k <= n <= 10^5
  • -10^4 <= nums[i] <= 10^4


Approach: This is clearly a sliding window problem. We can take sum of first k elements say 'sum' and then we slide the window by one i.e. remove first element of window from sum and add new element of the slided window to sum and record the max sum so far so every step looks like:

  • sum = sum - nums[start] + nums[i] 
  • start++
  • max_sum = MAX(sum, max_sum)


Implementation in C#:

        public double FindMaxAverage(int[] nums, int k)

    {
        int length = nums?.Length ?? 0;
        if (length < k)
        {
            return 0;
        }
        long sum = 0;
        int i = 0, start = 0;
        for (; i < k; ++i)
        {
            sum += nums[i];
        }
        long maxSum = sum;
        for (i = k; i < length; ++i)
        {
            sum = sum - nums[start++] + nums[i];
            maxSum = sum > maxSum ? sum : maxSum;
        }
        return (double)maxSum / k;
    }

Complexity: O(n)

Saturday, February 17, 2024

[LeetCode] Kids With the Greatest Number of Candies

Problem: There are n kids with candies. You are given an integer array candies, where each candies[i] represents the number of candies the ith kid has, and an integer extraCandies, denoting the number of extra candies that you have.

Return a boolean array result of length n, where result[i] is true if, after giving the ith kid all the extraCandies, they will have the greatest number of candies among all the kids, or false otherwise.

Note that multiple kids can have the greatest number of candies.

Example:

Input: candies = [2,3,5,1,3], extraCandies = 3
Output: [true,true,true,false,true] 
Explanation: If you give all extraCandies to:
- Kid 1, they will have 2 + 3 = 5 candies, which is the greatest among the kids.
- Kid 2, they will have 3 + 3 = 6 candies, which is the greatest among the kids.
- Kid 3, they will have 5 + 3 = 8 candies, which is the greatest among the kids.
- Kid 4, they will have 1 + 3 = 4 candies, which is not the greatest among the kids.
- Kid 5, they will have 3 + 3 = 6 candies, which is the greatest among the kids.
Input: candies = [4,2,1,1,2], extraCandies = 1
Output: [true,false,false,false,false] 
Explanation: There is only 1 extra candy.
Kid 1 will always have the greatest number of candies, even if a different kid is given the extra candy.
Input: candies = [12,1,12], extraCandies = 10
Output: [true,false,true]


Approach: A simple problem to solve. Get the maximum of the given candies first. Once we get it, we just need to compare candies[i] + extraCandies with max of candies. If it is equal to or more than max of candies then add true to result otherwise add false.


Implementation in C#:

    public IList<bool> KidsWithCandies(int[] candies, int extraCandies)
    {
        List<bool> result = new List<bool>();
        int maxCandy = this.findMaxCandy(candies);
        for (int i = 0; i < candies.Length; ++i)
        {
            result.Add(candies[i] + extraCandies >= maxCandy);
        }
        return result;
    }

    private int findMaxCandy(int[] candies)
    {
        int maxCandy = candies[0];
        for (int i = 1; i < candies.Length; ++i)
        {
            if (maxCandy < candies[i])
            {
                maxCandy = candies[i];
            }
        }
        return maxCandy;
    }


Complexity: O(n)

[LeetCode] Greatest Common Divisor of Strings

Problem: For two strings s and t, we say "t divides s" if and only if s = t + ... + t (i.e., t is concatenated with itself one or more times).

Given two strings str1 and str2, return the largest string x such that x divides both str1 and str2.

Example:

Input: str1 = "ABCABC", str2 = "ABC"
Output: "ABC"
Input: str1 = "ABABAB", str2 = "ABAB"
Output: "AB"
Input: str1 = "LEET", str2 = "CODE"
Output: ""


Approach: A simple approach would be to check for every prefix of the smaller string and check if the prefix is such that 

prefix + prefix + prefix + ... + prefix = str1

prefix + prefix + prefix + ... + prefix = str2

We can do some optimization like choosing a prefix such that the length of prefix is divisor of lengths of both str1 and str2 and may be more but whatever optimization we will do the complexity is going to be on higher side i.e. O(m^2 + n^2).

Let's try to see problem from different view, sometimes looking at the examples really help. If you see closely, you can find GCD of str1 and str2 can exist only if 

str1 + str2 = str2 + str1

Why? Let's say GCD of both strings is gcd then:

str1 = gcd + gcd + gcd + ... m times

str2 = gcd + gcd + gcd + ... n times

str1 + str2 = gcd + gcd + gcd + ... (m + n) times

str2 + str1 = gcd + gcd + gcd + ... (n + m) times

so you see str1 + str2 is equal to str2 + str1.

Now that we know when GCD exist, what is GCD of both strings? Just look at the example closely and you will quickly find that GCD is the prefix of length of GCD(Length(str1), Length(str2).

That's all!


Implementation in C#:

Brute-force:

    public string GcdOfStrings(string str1, string str2)
    {
        string gcd = string.Empty;
        int str1Len = str1.Length;
        int str2Len = str2.Length;
        int minStrLen = Math.Min(str1Len, str2Len);
        int currLen = 1;
        if (!string.IsNullOrEmpty(str1) && !string.IsNullOrEmpty(str2))
        {
            while (currLen <= minStrLen)
            {
                if (str1Len % currLen == 0 && str2Len % currLen == 0)
                {
                    string prefix = str1.Substring(0, currLen);
                    if (str2.StartsWith(prefix))
                    {
                        if (this.isStrRepetitionOfPrefix(str1, prefix)
                            && this.isStrRepetitionOfPrefix(str2, prefix))
                        {
                            gcd = prefix;
                        }
                    }
                }
                ++currLen;
            }
        }
        return gcd;
    }

    private bool isStrRepetitionOfPrefix(string str, string prefix)
    {
        int i = prefix.Length;
        while(i < str.Length)
        {
            if (str.Substring(i, prefix.Length) != prefix)
            {
                return false;
            }
            i += prefix.Length;
        }
        return true;
    }

Optimized:

    public string GcdOfStrings(string str1, string str2)
    {
        if (str1 + str2 != str2 + str1)
        {
            return string.Empty;
        }
        int maxLength = str1.Length;
        int minLength = str2.Length;
        if (maxLength < minLength)
        {
            minLength = maxLength;
            maxLength = str2.Length;
        }
        int gcdLength = this.getGCD(maxLength, minLength);
        return str1.Substring(0, gcdLength);
    }

    private int getGCD(int a, int b)
    {
        while (b != 0)
        {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

Complexity: Solution 1: O(m^2 + n^2)

                      Solution 2: O(m + n)

Friday, February 16, 2024

[LeetCode] Merge Strings Alternately

Problem: You are given two strings word1 and word2. Merge the strings by adding letters in alternating order, starting with word1. If a string is longer than the other, append the additional letters onto the end of the merged string.

Return the merged string.

Example:

Input: word1 = "abc", word2 = "pqr"
Output: "apbqcr"
Explanation: The merged string will be merged as so:
word1:  a   b   c
word2:    p   q   r
merged: a p b q c r
Input: word1 = "ab", word2 = "pqrs"
Output: "apbqrs"
Explanation: Notice that as word2 is longer, "rs" is appended to the end.
word1:  a   b 
word2:    p   q   r   s
merged: a p b q   r   s
Input: word1 = "abcd", word2 = "pq"
Output: "apbqcd"
Explanation: Notice that as word1 is longer, "cd" is appended to the end.
word1:  a   b   c   d
word2:    p   q 
merged: a p b q c   d


Approach: It's a simple problem to solve, you can look directly at the implementation to understand the approach.

    

Implementation in C#:

    public string MergeAlternately(string word1, string word2)
    {
        // Safety checks
        if (string.IsNullOrEmpty(word1) && string.IsNullOrEmpty(word2))
        {
            return word1;
        }
        if (string.IsNullOrEmpty(word1))
        {
            return word2;
        }
        if (string.IsNullOrEmpty(word2))
        {
            return word1;
        }

        int i = 0, j = 0;
        StringBuilder sb = new StringBuilder();
        while (i < word1.Length && j < word2.Length)
        {
            sb.Append(word1[i++]);
            sb.Append(word2[j++]);
        }

        if (i < word1.Length)
        {
            this.AppendAllChars(sb, i, word1);
        }
        if (j < word2.Length)
        {
            this.AppendAllChars(sb, j, word2);
        }

        return sb.ToString();
    }

    private void AppendAllChars(StringBuilder sb, int index, string word)
    {
        while (index < word.Length)
        {
            sb.Append(word[index++]);
        }
    }

Complexity: O(n)

Sunday, September 3, 2023

[LeetCode] Pancake Sorting

Problem: Given an array of integers arr, sort the array by performing a series of pancake flips.

In one pancake flip we do the following steps:

  • Choose an integer k where 1 <= k <= arr.length.
  • Reverse the sub-array arr[0...k-1] (0-indexed).

For example, if arr = [3,2,1,4] and we performed a pancake flip choosing k = 3, we reverse the sub-array [3,2,1], so arr = [1,2,3,4] after the pancake flip at k = 3.

Return an array of the k-values corresponding to a sequence of pancake flips that sort arr. Any valid answer that sorts the array within 10 * arr.length flips will be judged as correct.

Example:

Input: arr = [3,2,4,1]
Output: [4,2,4,3]
Explanation: 
We perform 4 pancake flips, with k values 4, 2, 4, and 3.
Starting state: arr = [3, 2, 4, 1]
After 1st flip (k = 4): arr = [1, 4, 2, 3]
After 2nd flip (k = 2): arr = [4, 1, 2, 3]
After 3rd flip (k = 4): arr = [3, 2, 1, 4]
After 4th flip (k = 3): arr = [1, 2, 3, 4], which is sorted.
Input: arr = [1,2,3]
Output: []
Explanation: The input is already sorted, so there is no need to flip anything.
Note that other answers, such as [3, 3], would also be accepted.

Constraints:

  • 1 <= arr.length <= 100
  • 1 <= arr[i] <= arr.length
  • All integers in arr are unique (i.e. arr is a permutation of the integers from 1 to arr.length).


Approach: We can try something like bubble sort approach where we put the maximum element at the end of the array. But how we do it? We are not allowed to swap, Right? We can only apply above Flip operation which basically reverse the sub array  [0...k - 1].

Let's see how we can use Flip. We find the index of maximum number say 'i'. Once we got it, we flip [0...i]. Now our maximum number is at the head. Now simply we can flip the whole array to take maximum number to last index.

Now we keep doing it for rest of the elements in descending order and apply these Flips. Please note that not every time we Flip the whole array as we know the maximum number is already in place so we decrease the last index by 1 every time we place the element to its right position.

That's all!


Implementation in C#:

    public IList<int> PancakeSort(int[] arr)
    {
        if (arr == null || arr.Length <= 1)
        {
            return new List<int>();
        }

        List<int> result = new List<int>();
        for (int i = arr.Length; i >= 1; --i)
        {
            int index = this.FindIndex(arr, i);
            if (index == i - 1)
            {
                continue;
            }
            if (index != 0)
            {
                result.Add(index + 1);
                this.Flip(arr, index);
            }
            result.Add(i);
            this.Flip(arr, i - 1);
        }
        return result;
    }

    private void Flip(int[] arr, int index)
    {
        int start = 0, end = index;
        while (start < end)
        {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            ++start;
            --end;
        }
    }

    private int FindIndex(int[] arr, int element)
    {
        for (int i = 0; i < arr.Length; ++i)
        {
            if (arr[i] ==  element)
            {
                return i;
            }
        }
        return -1;
    }

Complexity: O(n^2)