Wednesday, February 28, 2024

[LeetCode] Number of Recent Calls

Problem: You have a RecentCounter class which counts the number of recent requests within a certain time frame.

Implement the RecentCounter class:

  • RecentCounter() Initializes the counter with zero recent requests.
  • int ping(int t) Adds a new request at time t, where t represents some time in milliseconds, and returns the number of requests that has happened in the past 3000 milliseconds (including the new request). Specifically, return the number of requests that have happened in the inclusive range [t - 3000, t].

It is guaranteed that every call to ping uses a strictly larger value of t than the previous call.

Example:

Input
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
Output
[null, 1, 2, 3, 3]

Explanation
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1);     // requests = [1], range is [-2999,1], return 1
recentCounter.ping(100);   // requests = [1, 100], range is [-2900,100], return 2
recentCounter.ping(3001);  // requests = [1, 100, 3001], range is [1,3001], return 3
recentCounter.ping(3002);  // requests = [1, 100, 3001, 3002], range is [2,3002], return 3

Constraints:

  • 1 <= t <= 109
  • Each test case will call ping with strictly increasing values of t.
  • At most 104 calls will be made to ping.


Approach: We can use Queue to solve this problem. Basically we can take following steps in the Ping method:

  1. Enqueue t
  2. Keep Dequeueing till the Front of the queue < t - 3000.
  3. Return size of queue.

Implementation in C#:

public class RecentCounter
{
    public RecentCounter()
    {
        this.queue = new Queue<int>();  
    }
   
    public int Ping(int t)
    {
        while (queue.Count > 0 &&
               queue.Peek() < t - timeRange)
        {
            queue.Dequeue();
        }
        queue.Enqueue(t);
        return queue.Count;
    }

    private Queue<int> queue;
    private const int timeRange = 3000;
}

Complexity: O(timeRange)

[LeetCode] Removing Stars From a String

Problem: You are given a string s, which contains stars *.

In one operation, you can:

  • Choose a star in s.
  • Remove the closest non-star character to its left, as well as remove the star itself.

Return the string after all stars have been removed.

Note:

  • The input will be generated such that the operation is always possible.
  • It can be shown that the resulting string will always be unique.

Example:

Input: s = "leet**cod*e"
Output: "lecoe"
Explanation: Performing the removals from left to right:
- The closest character to the 1st star is 't' in "leet**cod*e". s becomes "lee*cod*e".
- The closest character to the 2nd star is 'e' in "lee*cod*e". s becomes "lecod*e".
- The closest character to the 3rd star is 'd' in "lecod*e". s becomes "lecoe".
There are no more stars, so we return "lecoe".
Input: s = "erase*****"
Output: ""
Explanation: The entire string is removed, so we return an empty string.


Approach: We can use stack here to solve this issue. We just need to iterate through the given string s and for every character we need to see if it is not '*' then push otherwise pop. After the loop ends, we need to pop all the remaining characters in the stack, store it in the string and reverse it. That will be our answer,

Just to save the space we can use the result string itself as stack like we did it in Remove All Adjacent Duplicates.

That's all!


Implementation in C#:

    public string RemoveStars(string s)
    {
        char[] stack = s.ToCharArray();
        int stackTop = -1;
        foreach (char ch in s)
        {
            if (ch != '*')
            {
                stack[++stackTop] = ch;
            }
            else
            {
                if (stackTop != -1)
                {
                    --stackTop;
                }
            }
        }
        if (stackTop == -1)
        {
            return "";
        }
        return new string(stack, 0, stackTop + 1);
    }

Complexity: O(n)


[LeetCode] Remove All Adjacent Duplicates in String II

Problem: You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them, causing the left and the right side of the deleted substring to concatenate together.

We repeatedly make k duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique.

Example:

Input: s = "abcd", k = 2
Output: "abcd"
Explanation: There's nothing to delete.
Input: s = "deeedbbcccbdaa", k = 3
Output: "aa"
Explanation: 
First delete "eee" and "ccc", get "ddbbbdaa"
Then delete "bbb", get "dddaa"
Finally delete "ddd", get "aa"


Approach: This problem is same as the problem Remove All Adjacent Duplicates In String. If you look closely in the previous problem k is always 2. We will use stack to solve this problem too. Here we won't just push the character but also we need to maintain the current continuous count of the character so if we see the character at the top is same as the current character, we increase its count and if we see the count is same as k then we pop that means we need to push a pair of character and it's count in stack. If the characters are not same then we safely push the pair of current character and 1(current count).

We can solve it without using stack to like we did before however I don't see much of benefit here as we need to maintain a count array anyways so extra storage will be there. I have given this implementation too. 


Implementation in C#:

Using Stack:

    public string RemoveDuplicates(string s, int k)
    {
        int length = s?.Length ?? 0;
        if (length < k)
        {
            return s;
        }
        var stack = new Stack<KeyValuePair<char, int>>();
        foreach (char ch in s)
        {
            if (stack.Count == 0 || stack.Peek().Key != ch)
            {
                stack.Push(new KeyValuePair<char, int>(ch, 1));
            }
            else
            {
                var stElem = stack.Pop();
                if (stElem.Value < k - 1)
                {
                    stack.Push(new KeyValuePair<char, int>(ch, stElem.Value + 1));
                }
            }
        }
        if (stack.Count == 0)
        {
            return "";
        }
        StringBuilder result = new StringBuilder();
        while (stack.Count != 0)
        {
            var stElem = stack.Pop();
            for (int i = 0; i < stElem.Value; ++i)
            {
                result.Append(stElem.Key);
            }
        }
        return new string(result.ToString().Reverse().ToArray());
    }

Without using stack:

    public string RemoveDuplicates(string s, int k)
    {
        int length = s?.Length ?? 0;
        if (length < k)
        {
            return s;
        }
        char[] stack = s.ToCharArray();
        int[] count = new int[stack.Length];
        int stackTop = -1;
        foreach (char ch in s)
        {
            if (stackTop == -1 || stack[stackTop] != ch)
            {
                stack[++stackTop] = ch;
                count[stackTop] = 1;
            }
            else
            {
                if (count[stackTop] < k - 1)
                {
                    ++count[stackTop];
                }
                else
                {
                    --stackTop;
                }
            }
        }
        if (stackTop == -1)
        {
            return "";
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i <= stackTop; ++i)
        {
            for (int j = 0; j < count[i]; ++j)
            {
                result.Append(stack[i]);
            }
        }
        return result.ToString();
    }

Complexity: O(n)

[LeetCode] Remove All Adjacent Duplicates In String

Problem: You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two adjacent and equal letters and removing them.

We repeatedly make duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique.

Example:

Input: s = "abbaca"
Output: "ca"
Explanation: 
For example, in "abbaca" we could remove "bb" since the letters are adjacent and equal, and this is the only possible move.  The result of this move is that the string is "aaca", of which only "aa" is possible, so the final string is "ca".
Input: s = "azxxzy"
Output: "ay"


Approach: We can use stack to solve this problem as we need to repeatedly remove duplicates. The solution is simple; We iterate through the input string s, if the top of the stack matches with the current character, we just pop otherwise we push current character into stack. Once the loop is completed, we can take all the characters from the stack into a string and reverse it. That will be our answer.

The above apporach works and is very efficient, but if you see we are using extra storage in the form of stack. Can we do better?

We can use our result string as stack. We just need to recall how to implement stack using an array and in that way we will be able to save space.

That's all!


Implementation in C#:

Using Stack:

    public string RemoveDuplicates(string s)
    {
        int length = s?.Length ?? 0;
        if (length <= 1)
        {
            return s;
        }
        Stack<char> stack = new Stack<char>();
        foreach(char ch in s)
        {
            if (stack.Count == 0 || stack.Peek() != ch)
            {
                stack.Push(ch);
            }
            else
            {
                stack.Pop();
            }
        }
        if (stack.Count == 0)
        {
            return "";
        }
        char[] result = new char[stack.Count];
        int index = stack.Count - 1;
        while (stack.Count != 0)
        {
            result[index--] = stack.Pop();
        }
        return new string(result);
    }

Without using stack:

        public string RemoveDuplicates(string s)

    {
        int length = s?.Length ?? 0;
        if (length <= 1)
        {
            return s;
        }
        char[] result = s.ToCharArray();
        int stackTop = -1;
        foreach(char ch in s)
        {
            if (stackTop == -1 || result[stackTop] != ch)
            {
                result[++stackTop] = ch;
            }
            else
            {
                --stackTop;
            }
        }
        if (stackTop == -1)
        {
            return "";
        }
        return new string(result, 0, stackTop + 1);
    }

Complexity: In both the cases O(n)

[LeetCode] Equal Row and Column Pairs

Problem: Given a 0-indexed n x n integer matrix grid, return the number of pairs (ri, cj) such that row ri and column cj are equal.

A row and column pair is considered equal if they contain the same elements in the same order (i.e., an equal array).

Example:

Input: grid = [[3,2,1],[1,7,6],[2,7,7]]
Output: 1
Explanation: There is 1 equal row and column pair:
- (Row 2, Column 1): [2,7,7]

Input: grid = [[3,1,2,2],[1,4,4,5],[2,4,2,2],[2,4,2,2]]
Output: 3
Explanation: There are 3 equal row and column pairs:
- (Row 0, Column 0): [3,1,2,2]
- (Row 2, Column 2): [2,4,2,2]
- (Row 3, Column 2): [2,4,2,2]


Approach: We can solve it by comparing every row to every column but it will be expensive. We can reduce the time complexity by using hash but how? How can we cache a whole row?

What we can do is we can convert every row and column into a string using a separator so if the row is say | 12 | 30 | 48 | then converted string will be "12#30#48#". Now this converted string can be used as key of the hash, the value of the hash will be the frequency of such row.

With the above approach what we can do is we can hash every row first into the HashTable using the converted string as key. Once we are done with it, we can iterate over every column, convert into the string using same method and if the hash table contains this string then we can add it's value to the result.

That's all!


Implementation in C#:

    public int EqualPairs(int[][] grid)
    {
        int rows = grid?.Length ?? 0;
        if (rows == 0)
        {
            return 0;
        }
        if (rows == 1)
        {
            return 1;
        }
        int cols = grid[0].Length;
        Dictionary<string, int> rowStrs = new Dictionary<string, int>();
        for (int i = 0; i < rows; ++i)
        {
            StringBuilder rowStr = new StringBuilder();
            for (int j = 0; j < cols; ++j)
            {
                rowStr.Append(grid[i][j].ToString());
                rowStr.Append("#");
            }
            string str = rowStr.ToString();
            if (!rowStrs.ContainsKey(str))
            {
                rowStrs[str] = 0;
            }
            ++rowStrs[str];
        }
        int result = 0;
        for (int j = 0; j < cols; ++j)
        {
            StringBuilder colStr = new StringBuilder();
            for (int i = 0; i <  rows; ++i)
            {
                colStr.Append(grid[i][j].ToString());
                colStr.Append("#");
            }
            string str = colStr.ToString();
            if (rowStrs.ContainsKey(str))
            {
                result += rowStrs[str];
            }
        }
        return result;
    }

Complexity: O(n^2 * log(n))

Tuesday, February 27, 2024

[LeetCode] Unique Number of Occurrences

Problem: Given an array of integers arr, return true if the number of occurrences of each value in the array is unique or false otherwise.

Example:

Input: arr = [1,2,2,1,1,3]
Output: true
Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences.
Input: arr = [1,2]
Output: false
Input: arr = [-3,0,1,-3,1,1,1,-3,10,0]
Output: true


Approach: We can use sorting to solve this problem but it will take nlogn time. To solve it in less time we can use a Map and a Set. First in the Map we will store the frequency of each element in the array and then we will traverse the Map and see if there are any repeated values in it using the Set.

That's all!


Implementation in C#:

    public bool UniqueOccurrences(int[] arr)
    {
        int length = arr?.Length ?? 0;
        if (length <= 1)
        {
            return true;
        }
        Dictionary<int, int> freqMap = new Dictionary<int, int>();
        foreach (int num in arr)
        {
            if (!freqMap.ContainsKey(num))
            {
                freqMap[num] = 0;
            }
            ++freqMap[num];
        }
        HashSet<int> freqSet = new HashSet<int>();
        foreach (int key in freqMap.Keys)
        {
            if (!freqSet.Add(freqMap[key]))
            {
                return false;
            }
        }
        return true;
    }

Complexity: O(n)

[LeetCode] Find the Difference of Two Arrays

Problem: Given two 0-indexed integer arrays nums1 and nums2, return a list answer of size 2 where:

  • answer[0] is a list of all distinct integers in nums1 which are not present in nums2.
  • answer[1] is a list of all distinct integers in nums2 which are not present in nums1.

Note that the integers in the lists may be returned in any order.

Example:

Input: nums1 = [1,2,3], nums2 = [2,4,6]
Output: [[1,3],[4,6]]
Explanation:
For nums1, nums1[1] = 2 is present at index 0 of nums2, whereas nums1[0] = 1 and nums1[2] = 3 are not present in nums2. Therefore, answer[0] = [1,3].
For nums2, nums2[0] = 2 is present at index 1 of nums1, whereas nums2[1] = 4 and nums2[2] = 6 are not present in nums2. Therefore, answer[1] = [4,6].
Input: nums1 = [1,2,3,3], nums2 = [1,1,2,2]
Output: [[3],[]]
Explanation:
For nums1, nums1[2] and nums1[3] are not present in nums2. Since nums1[2] == nums1[3], their value is only included once and answer[0] = [3].
Every integer in nums2 is present in nums1. Therefore, answer[1] = [].


Approach: We can use HashSets to solve this issue. Just look at the implementation to understand the solution as it is straight forward.


Implementation in C#:

    public IList<IList<int>> FindDifference(int[] nums1, int[] nums2)
    {
        HashSet<int> num1Set = new HashSet<int>(nums1);
        HashSet<int> num2Set = new HashSet<int>(nums2);
        List<int> numsToRemove = new List<int>();
        foreach (int num in num1Set)
        {
            if (num2Set.Contains(num))
            {
                numsToRemove.Add(num);
            }
        }
        foreach (int num in numsToRemove)
        {
            num1Set.Remove(num);
            num2Set.Remove(num);
        }
        return new List<IList<int>> { num1Set.ToList(), num2Set.ToList() };
    }

Complexity: O(n)