Thursday, October 8, 2020

Bitwise AND of Numbers Range

Problem: Given a range [low, high] where low and high are positive integers and low < = high, return the bitwise AND of all numbers in this range, inclusive.

Example:

Input: [26,30]
Output: 24


Approach: One trivial way is we can use a loop from low to high and do the AND but we want to do better. Lets have a close look, say for number 26 to 30 -

26 - 11010

27 - 11011

28 - 11100

29 - 11101

30 - 11110

As per AND operator property, AND of numbers will produce a set bit(1) at position i only if all the numbers have set bit at position i otherwise it will be 0. As these numbers are in increasing order and the difference between numbers is 1, it can be easily observed that the numbers with in this range except low and high are not important and if the AND of these numbers is not zero then some bits till position j from left in low and high are going to be same that means if we keep right shifting low and high, at some point low and high are going to be same. Here in our case we will keep right shifting the 26 and 30 by 1 till they become same which is 00011(3) after 3 right shifts so what we can say that AND of range (26,30) is 00011 << 3 = 11000 (24).


Implementation in C#:

        public int RangeBitwiseAnd(int low, int high)

        {

            int count = 0;

            while (low != high)

            {

                low >>= 1;

                high >>= 1;

                ++count;

            }

            return low << count;

        }  


Complexity: O(n) where n is the number of bits.

Wednesday, October 7, 2020

House Robber

Problem: You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example:

Input: nums = [3,6,11,7,1]
Output: 15
Explanation: Rob house 1 (amount = 3), rob house 3 (amount = 11) and rob house 5 (amount = 1).
             Total amount you can rob = 3 + 11 + 1 = 15.


Approach: We can see it is a DP problem. Let's have an array Table where Table[i] will tell the maximum amount of money can be robbed till ith house. We can fill the Table in following way:

  1. Table[0] = nums[0] as only one house is there.
  2. Table[1] = Max(nums[0], nums[1]). In case of 2 house we just collect the money from the house which has the maximum amount of money.
  3. Table[i] = Max(Table[i - 1], Table[i - 2] + nums[i]). Here what we are doing is at every house we are deciding which amount is greater; maximum amount collected till before previous house(Table[i - 2]) + amount in current house(nums[i]) or maximum amount collected till previous house(Table[i - 1]).
Obviously Table[n - 1] will be our answer. Can we do any improvement? Yes, If we look closely we are just using (i - 1) and (i - 2) values to decide the current(i) amount of money so we can use just 2 variables to hold these values instead of having the whole table. In this way our space complexity will become O(1) instead of O(n).

Implementation in C#:

    public int Rob(int[] nums)
    {
        int length = nums?.Length ?? 0;
        if (length == 0)
        {
            return 0;
        }
        if (length == 1)
        {
            return nums[0];
        }
        if (length == 2)
        {
            return Math.Max(nums[0], nums[1]);
        }
        int prev1 = nums[0];
        int prev2 = Math.Max(nums[0], nums[1]);
        for (int i = 2; i < length; ++i)
        {
            int curr = Math.Max(prev1 + nums[i], prev2);
            prev1 = prev2;
            prev2 = curr;
        }
        return prev2;
    }


Complexity: O(n)

Monday, October 5, 2020

Number of set bits in an integer (Hamming weight)

Example:

Input: 00000000000000000000000000000101
Output: 2
Explanation: The input binary string 00000000000000000000000000000101 has a total of two '1' or set bits.

Approach: Using Brian Kernighan algorithm. Basically this algorithm relies on the fact that subtracting '1' from a number flips all the bits after the rightmost set bit including the rightmost set bit so if we do n & (n - 1), it will unset the rightmost set bit. 

Now we can have a loop until n becomes 0 by doing n = n & (n - 1) that means 1 by 1 all the (rightmost) set bits will be unset and n will become 0. Obviously the number of times loop will run will be our answer.

Implementation in C#:

        public int NumOfSetBits(uint n)

        {

            int count = 0;

            while(n > 0)

            {

                n = n & (n - 1);

                ++count;

            }

            return count;

        }


Complexity: O(logn)

Reverse bits of a given 32 bits unsigned integer

Example (Taken from leetcode):

Input: 11111111111111111111111111111101
Output: 10111111111111111111111111111111
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471 which its binary representation is 10111111111111111111111111111111.

Approach: There are many ways to solve this problem. O(n) when n is number of bits is very trivial solution. Let's talk about a better solution:

Lets take an example of 4 bit integer first for simplicity say 13 whose 4 bit representation will be 1101. What we will try to do it is first we will reverse number by 4/2 = 2 bits and then 2/2 = 1 bit:

  1. At step 1, reverse the number by 2 bits that so the number will become 01|11 => 0111. Can be done by n = ((n & 0xc) >> 2) | ((n & 0x3) << 2); 
  2. At step 2, we will reverse the number 1 by 1 bit so now the number 0111 will become 1|0|1|1 => 1011. Can be done by n = ((n & 0xa) >> 1) | ((n & 0x5) << 1);
And that's it. n will be our answer so now let's take an example of 8 bit number say 10011110. Now we are going to do it in 3 steps:
  1. At step1, reverse the number by 8/2 = 4 bits so the number will become 1110|1001 => 11101001. Can be done by n = ((n & 0xf0) >> 4) | ((n & 0x0f) << 4);
  2. At step 2, reverse the number by 4/2 = 2 bits that so the number 11101001 will become 10|11|01|10 => 10110110. Can be done by n = ((n & 0xcc) >> 2) | ((n & 0x33) << 2); 
  3. At step 3, we will reverse the number 1 by 1 bit so now the number 10110110 will become 0|1|1|1|1|0|0|1 => 01111001. Can be done by n = ((n & 0xaa) >> 1) | ((n & 0x55) << 1);
If you see the step 2 and step 3 for 8 bit integer reversal is same as step 1 and step 2 of 4 bit integer reversal. Now hopefully we understood what we are trying to do so lets see the steps for 32 bit integer:
  1. Reverse the number by 32/2 = 16 bits by 16 bits.
  2. Reverse the number by 16/2 = 8 bits by 8 bits.
Rest of the steps will be same as 8 bit numbers given above. 


Implementation in C#:

        public uint ReverseBits(uint n)
        {
            n = ((n & 0xffff0000) >> 16) | ((n & 0x0000ffff) << 16);
            n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);
            n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);
            n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);
            n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);
            return n;
        }  


Complexity: O(logn) where n is the number of bits.

Saturday, October 3, 2020

Rotate an array

Problem: Given an array, rotate the array to the right by k steps, where k is non-negative.

Example (taken from leetcode):

Input: nums = [1,2,3,4,5,6,7], k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]


Approach: It  can be achieved in three steps:

  • Reverse the whole array. 
  • Reverse 1st k elements.
  • Reverse the rest of n-k elements. That means reverse subarray arr[k...n-1]

Now k could be more than n so we can initialize k as k = k mod n.


Implementation in C#:

    public void Rotate(int[] nums, int k)
    {
        int length = nums?.Length ?? 0;
        if (length <= 1)
        {
            return;
        }
        k = k % length;
        this.Reverse(nums, 0, length - 1);
        this.Reverse(nums, 0, k - 1);
        this.Reverse(nums, k, length - 1);
    }

    private void Reverse(int[] nums, int start, int end)
    {
        while (start < end)
        {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            ++start;
            --end;
        }
    }


Complexity: O(n)

Friday, October 2, 2020

Best Time to Buy and Sell Stock with at most k transactions

Problem: Say you have an array for which the i-th element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most k transactions. Please note that you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Example:

Input: [2,1,2,0,1], k = 2
Output: 2
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 2), profit = 2-1 = 1.
             Then buy on day 4 (price = 0) and sell on day 5 (price = 1), profit = 1-0 = 1.                 so total profit is 1 + 1 = 2.


Approach: Like previous buy and sell stock problems, we are going to use DP here too. Lets take a 2D Table where Table[i][j] will tell the maximum profit on jth day by doing at most i sales. Obviously Table[k][n - 1] where n is size of array will be our answer. Here is how we are going to fill our table:

  1. Table[i][0] = 0 for all i = 0 to k as on day 0, profit will be 0 as no sale can be performed.
  2. Table[0][i] = 0 for all i = 0  to n - 1 as no sales have been performed so profit will be 0.
  3. Table [i][j] = MAX (Table[i][j -1], Max(prices[j] - prices[d] + Table[i - 1][d]) for all d = 0 to j - 1)
Let me explain point 3 in detail;  At each day we have 2 choices -
  1. Do not sell the stock: Table[i][j] = Table[i][j - 1], the profit will remain same as the previous day with same number of sales.
  2. Sell the stock: In that case we need to see with each and every day's (before j) price and profit with 1 less sale:
    1. prices[j] - prices[d] to get the current profit
    2. Table[i - 1][d] to get the maximum profit till day d with i - 1 sales.
  3. So now you can see we need to get the maximum of prices[j] - prices[d] + Table[i - 1][d] for all d = 0 to j - 1.
That's it. This will solve our problem in O(k * n ^ 2) which is good but we can still do better. Let's see how:

Table[i][j] = MAX (Table[i][j -1], Max(prices[j] - prices[d] + Table[i - 1][d]) for all d = 0 to j - 1)

Max(prices[j] - prices[d] + Table[i - 1][d]) for all d = 0 to j - 1

=  prices[j] + Max(Table[i-1][d] - prices[d]) for all d = 0 to j - 1

= prices[j] + Max(PrevMax, Table[i - 1][j - 1] - prices[j - 1]) 

Here PrevMax is Max(Table[i - 1][d] - prices[d]) for all d = 0 to j - 2. so now we can say:

Table[i][j] = MAX (Table[i][j -1], prices[j] + Max(PrevMax, Table[i - 1][j - 1] - prices[j - 1]))

The only problem is how we calculate the PrevMax in constant time or I would say how to have it calculated already for d = 0 to j - 2 so that we don't have to look back always. If you look at the implementation below then you will understand that for every i transaction we can calculate the PrevMax for all days (0....n) in the same loop to get the profit with i - 1 transactions.


Implementation in C#:

        public static int MaxProfit(int k, int[] prices)
        {
            if (prices?.Length <= 1 || k <= 0)
            {
                return 0;
            }

            int result = 0;

            if (k > prices.Length / 2) // special condition, we can do transactions as many as we want.
            {
                for(int i = 1; i < prices.Length; ++i)
                {
                    result += Math.Max(0, prices[i] - prices[i - 1]);
                }
                return result;
            }

            int[,] table = new int[k + 1, prices.Length];

            for (int i = 0; i <= k; ++i)
            {
                table[i, 0] = 0;
            }

            for (int i = 0; i < prices.Length; ++i)
            {
                table[0, i] = 0;
            }

            for (int i = 1; i <= k; ++i)
            {
                int prevMax = int.MinValue;
                for (int j = 1; j < prices.Length; ++j)
                {
                    prevMax = Math.Max(prevMax, table[i - 1, j - 1] - prices[j - 1]);
                    table[i, j] = Math.Max(table[i, j - 1], prices[j] + prevMax);
                }
            }

            return table[k, prices.Length - 1];
        }

Complexity: O(k * n)

Another approach: We can think of solution in another way too. Just a different perspective. We can have a 3D Table where Table[i][j][h] will tell the maximum profit on ith day with j buying transactions and h here could be 0 or 1 which will tell whether we are holding stocks or not. Finally max of Table[n-1][j][0] where j = 0 to k will be our answer. That is profit at the last day with at most k (j = 0 to k) sales without holding stock will be our answer. Here is how we fill the Table:
  1. Table[0][0][0] = 0
  2. Table[0][1][1] = -prices[0]; // Hold the stock means we purchase (1 transaction) the stock
  3. Now at every day, we can take following four actions:
    1. Keep holding the stock: Table[i][j][1] = Table[i - 1][j][1] // No change from last day to current day
    2. Keep not holding the stock: Table[i][j][0] = Table[i - 1][j][0]  // No change from last day to current day
    3. Buy the stock: Table[i][j][1] = Table[i - 1][j - 1][0] - prices[i] // Now we will hold the stock h = 0, we go to previous day's max profit with one less buying transactions without holding stocks and subtract the current day's stock price (buy the stock)
    4. Sell the stock: Table[i][j][0] = Table[i-1][j][1] + prices[i] // Now we will hold the stock h = 1, we go ot previous day's max profit with same number of buying transactions but with holding the stock (as we need to sell) and add the current day's stock price as we are selling it.
  4. Now if you see, we can combine the above four points into 2:
    1. Table[i][j][0] =  max (Table[i-1][j][0], Table[i-1][j][1] + prices[i])
    2. Table[i][j][1] = max (Table[i-1][j][1], Table[i-1][j-1][0] - prices[i])
That's it. 

Implementation in C#:

        public static int MaxProfit(int k, int[] prices)
        {
            if (prices?.Length <= 1 || k <= 0)
            {
                return 0;
            }

            int result = 0;

            if (k > prices.Length)
            {
                for(int i = 1; i < prices.Length; ++i)
                {
                    result += Math.Max(0, prices[i] - prices[i - 1]);
                }
                return result;
            }

            int[,,] table = new int[prices.Length, k + 1, 2];

            for (int i = 0; i < prices.Length; ++i)
            {
                for (int j = 0; j <= k; ++j)
                {
                    table[i, j, 0] = -1000000000; // int.Min can result into integer overflow
                    table[i, j, 1] = -1000000000;
                }
            }

            table[0, 0, 0] = 0;
            table[0, 1, 1] = -prices[0];

            for (int i = 1; i < prices.Length; ++i)
            {
                for (int j = 0; j <= k; ++j)
                {
                    table[i, j, 0] = Math.Max(table[i - 1, j, 0], table[i - 1, j, 1] + prices[i]);
                    if (j > 0)
                    {
                        table[i, j, 1] = Math.Max(table[i - 1, j, 1], table[i - 1, j - 1, 0] - prices[i]);
                    }
                }
            }

            for (int i = 0; i <= k; ++i)
            {
                result = Math.Max(result, table[prices.Length - 1, i, 0]);
            }

            return result;
        }

Complexity: O(n * k)

Thursday, October 1, 2020

Repeated DNA Sequences

Problem: All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA.

Write a method to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule.

Example (taken from leetcode):

Input: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"

Output: ["AAAAACCCCC", "CCCCCAAAAA"]


Approach: Sliding window with hashing. Simply take each possible 10 character substring and put it in the hash. If a substring coming for the second time, just add it into result.


Implementation in C#:

        public IList<string> FindRepeatedDnaSequences(string s)

        {

            List<string> result = new List<string>();

            Dictionary<string, int> hash = new Dictionary<string, int>();

            for (int i = 0; i + 10 <= s.Length; ++i)

            {

                string key = s.Substring(i, 10);

                hash[key] =  hash.ContainsKey(key) ? hash[key] + 1 : 1;

                if (hash[key] == 2)

                {

                    result.Add(key);

                }

            }

            return result;

        } 


Complexity: O(10*n) => O(n)