Showing posts with label bit manipulation. Show all posts
Showing posts with label bit manipulation. Show all posts

Thursday, December 17, 2015

Maximum Product of Word Lengths [LeetCode]

Problem Description
Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.
Example 1:
Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"]
Return 16
The two words can be "abcw", "xtfn".
Example 2:
Given ["a", "ab", "abc", "d", "cd", "bcd", "abcd"]
Return 4
The two words can be "ab", "cd".
Example 3:
Given ["a", "aa", "aaa", "aaaa"]
Return 0
No such pair of words.
Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.
Solution

If you first read the problem, you can think of brute force solution: for each pair of words, check whether they have a common letter, if not, get the product of their lengths and compare to max value achieved so far.
The brute force solution leads to another requirement: checking a pair of words if they contain common letters? Actually, we can do that with some pre-calculation, and with the understanding that the words contain only lowercase letters.
Since there are only 26 lowercase letters, we can represent a set of letters using an integer. So let's say if the word contains 'a', then the integer's 0th bit will be 1. If it has 'b', then the 1st is set to 1, so on and so forth.

Below is the Java code with simple implementation.
I. Simple Solution
public class Solution {
    //In this code, I used dietpepsi as array's name to give credit to dietpepsi ^_^
    public int maxProduct(String[] words) {
        int n = words.length;
        int[] dietpepsi = new int[n];
        for(int i=0; i<n; i++){
            dietpepsi[i] = getMask(words[i]);
        }
        int max = 0; int t;
        for(int i=0; i<n; i++){
            t = 0;
            for(int j=i+1; j<n; j++){
                if((dietpepsi[i] & dietpepsi[j]) == 0){
                    t = Math.max(t, words[j].length());
                }
            }
            max = Math.max(max, t*words[i].length());
        }
        return max;
    }
    private int getMask(String s){
        int mask = 0;
        for(char c: s.toCharArray()){
            mask |= 1 << (c - 'a');
        }
        return mask;
    }
}
II. Improvement on (I)
We can make some improvement by first sorting the words according to their lengths. Then for the ith word in the sorted array, we check from i-1 to 0 to see if there is a word that shares no common letter with it. Then we calculate the product, compare to the max value so far, stop the loop for the ith word, and move on with the (i+1)th word.
public class Solution {
    public int maxProduct(String[] words) {
        int n = words.length;
        
        Arrays.sort(words, new LengthComparator());
        int[][] dietpepsi = new int[n][2];
        int max = 0;
        for(int i=0; i<n; i++){
            dietpepsi[i][0] |= getMask(words[i]); 
            dietpepsi[i][1] = words[i].length();
        }
        
        int last = 0;
        for(int i=n-1; i>=1; i--){
            for(int j=i-1; j>=last; j--){
                if((dietpepsi[i][0] & dietpepsi[j][0]) == 0){
                    max = Math.max(dietpepsi[i][1] * dietpepsi[j][1], max);
                    last = j;
                    while(last<n && dietpepsi[last][1]==dietpepsi[j][1]) last++;
                    break;
                }
            }
        }
        return max;
    }
    private int getMask(String s){
        int mask = 0;
        for(char c: s.toCharArray()){
            mask |= 1 << (c - 'a');
        }
        return mask;
    }
    class LengthComparator implements Comparator<String>{
        public int compare(String a, String b){
            return a.length() - b.length();
        }
    }
}
PS: Do you think there is a O( NLgN) or O(N) solution for this problem?

Tuesday, September 1, 2015

Missing Number [LeetCode]

Problem Description
Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array.
For example,
Given nums = [0, 1, 3] return 2.
Note:
Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space complexity?
Solution
Again, similar to other bit manipulation problems, we can use XOR operator. One NOTE about XOR:
1. if A ^ B = C then A = B ^ C, and B = A ^ C,
2. and A^A = 0, 0 ^ A = A,
where ^ is XOR bit operator.
Since nums contains numbers from 0 to n with one number is missing, then if we xor all the numbers in nums with 0 ^ 1 ^ 2 ... ^ n, we will get the number we need to find.
Below is the codes.

I. Java Code
public class Solution {
    public int missingNumber(int[] nums) {
        int r = 0;
        for (int i = 0; i<nums.length; i++){
            r ^=  i ^ nums[i];
        }
        return r ^ nums.length;
    }
}
II. Python Code
class Solution(object):
    def missingNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        r = 0
        for i in range(len(nums)):
            r ^= i ^ nums[i]
        return r ^ len(nums)
III. C++ Code
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int r = 0; 
        for (int i = 0; i<nums.size(); i++){
            r ^= i ^ nums[i];
        }
        return r ^ nums.size();
    }
};
IV. Javascript Code
/**
 * @param {number[]} nums
 * @return {number}
 */
var missingNumber = function(nums) {
    var r = 0;
    for(var i = 0; i<nums.length; i++){
        r ^= i ^ nums[i];
    }
    return r ^ nums.length;
};
Appendix A: Other methods
We can use other methods such as Hash Table with O(n) space complexity. We also can get the number by summing up all the elements in the array, the take the difference of n(n+1)/2 and that sum. (NOTE: n(n+1)/2 is the sum of numbers from 0 to n).

Single Number III [LeetCode]

Problem Description
Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
For example:
Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].
Note:
  1. The order of the result is not important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
Solution 
Again, Hash Table solution is not discussed here.
This problem is an extension of the other two problems Single Number and Single Number II. As we know that for an integer N:
0 ^ N = N
N ^ N = 0
where ^ is XOR bit operator.
If we call the two numbers that we need to find is A and B. We all know that if we XOR all the elements in the provided array, we will get A ^ B. This is a lot of information!
Now, we notice that A and B must be different at some bit at position t in their binary representations. So if we divide the set of numbers into 2 set, one is the set of all the numbers that have the same bit at position t as A, the other is the set of all numbers that have the same bit at position t as B. These 2 sub-sets have a special characteristic: all numbers appear 2 times, except 1. This bring us to the Single Number problem.

I. Java Code
public class Solution {
    public int[] singleNumber(int[] nums) {
        int A = 0;
        int B = 0;
        int AXORB = 0;
        for(int i = 0; i<nums.length; i++){
            AXORB ^= nums[i];
        }
        
        AXORB = (AXORB & (AXORB - 1)) ^ AXORB; //find the different bit
        for(int i = 0; i<nums.length; i++){
            if((AXORB & nums[i]) == 0)
                A ^= nums[i];
            else
                B ^= nums[i];
        }
        return new int[]{A, B};
    }
}

Sunday, August 30, 2015

Single Number II : How to come up with Bit Manipulation Formula

Problem Description
Given an array of integers, every element appears three times except for one. Find that single one.
Note:
Your algorithm should have a linear running time complexity. Could you implement it without using extra memory?
Solutions
By following up Single Number problem we can solve this problem by using Hash Table. So we will not discuss that solution here.
At first, in order to solve the problem, we try to make it simpler. We only consider the right most bit of each number, and count the number of bits that equals to 1. Call "mars" is the number of 1 bits. We also call "elon" is the number that appear only once, and "elonBit" is the rightmost bit of "elon" (The terms are just for fun because Elon Musk is my idol :)).

Our observation is that if "mars" is a multiple of 3 then "elonBit" must be 0,  otherwise it is 1. We can repeat this procedure for all other bits. This is what we have in the following code.

I. Counting the number of bit 1.
public class Solution {
    public int singleNumber(int[] nums) {
        
        int result = 0;
        for(int i = 0; i<32; i++){
            int elonBit = 1 << i;
            int mars = 0;
            for(int j=0; j<nums.length; j++){
                if((nums[j] & elonBit) != 0) // if it is bit 1
                    mars++;
            }
            if (mars % 3 == 1) result |= elonBit;
        }
        return result;
    }
}
However, we see that the actual value of "mars" is not really important. Every time "mars" equal to 3, we can reset "mars" to 0. Since we are working with bits, we can represent "mars" by 2 bits and q.

Since with 2 bits, we can represent 4 numbers, so we have different ways to represent the states from 0 to 2 (3 states). We will pick the natural way: 00 for state 0, 01 for state 1, and 10 for 2 (NOTE: if we choose another way to represent, we may come up with different solution later, so you should try it ^_^). With this way of representing, we see that when mars % 3 = 0, pq = 00, and when mars % 3 = 1, pq = 01. Therefore, the final value of q will be exactly the value of "elonBit".

Now we call r is the corresponding bit of a number in the array. If r = 1, we have the following transitions: (00 + 1 = 01), (01 + 1 = 10), (10 + 1 = 00). (The + means the transition with condition that we met bit 1).

We have the following transition table, where (old_p, old_q) represents the state before we examine a number, and (p, q) represents the state after that.
_____________________________
|old_p |old_q |   r    |    p      | q        |
--------------------------------------------
|   0     |   0     |   0    |    0     | 0         |
--------------------------------------------
|   0     |   1     |   0    |    0     | 1         |
--------------------------------------------
|   1     |   0     |   0    |    1     | 0         |
--------------------------------------------
|   0     |   0     |   1    |    0     | 1         |
--------------------------------------------
|   0     |   1     |   1    |    1     | 0         |
--------------------------------------------
|   1     |   0     |   1    |    0     | 0         |
--------------------------------------------

Based on the above transition table, we can derive the formula of and based on old_p, old_q, and r.
p = (old_p & ~old_q & ~r) | (~old_p & old_q & r)
q = (~old_p & old_q& ~r) | (~old_p & ~old_q & r)

Based on this, we jump directly to coding and have the following code.
II. Method 2
public class Solution {
    public int singleNumber(int[] nums) {
        
       int p = 0;
       int q = 0;
       
       int old_p, old_q, r;
       for(int i = 0; i<nums.length; i++){
           old_p = p; old_q = q;
           r = nums[i]; //this to make it consistent with our analysis.
           p = (old_p & ~old_q & ~r) | (~old_p & old_q & r);
           q = (~old_p & old_q& ~r) | (~old_p & ~old_q & r);
       }
       return q;
    }
}

Now, we see that in this assignment:
p = (old_p & ~old_q & ~r) | (~old_p & old_q & r)
we have old_q = q, and old_p is p before that assignment.
So the formula become:
p = (p & ~q& ~r) | (~p& q& r)

As a consequence, we have the code:
III. Method 3: Improvement on Method 2
public class Solution {
    public int singleNumber(int[] nums) {
       int p = 0;
       int q = 0;
       int r, old_p;
       for(int i = 0; i<nums.length; i++){
          r = nums[i]; //this to make it consistent with our analysis. 
          old_p = p;
          p = (p & ~q& ~r) | (~p& q& r);
          q = (~old_p & q& ~r) | (~old_p & ~q & r);
       }
       return q;
    }
}
In the above code, it is still ugly because we need to use old_p. Why we cannot use p to replace old_p? Let's look at the triple (old_q, r, p) in the transition table. Its values include (0, 0, 0), (1, 0, 0), (0, 0, 1), (0, 1, 0), (1, 1, 1), (0, 1, 0). The triple (0, 1, 0) is duplicated, therefore information is lost for this case.
We can try different transition tables!
|old_p |old_q |   r    |    p      | q        |
--------------------------------------------
|   0     |   0     |   0    |    0     | 0         |
--------------------------------------------
|   0     |   1     |   0    |    0     |         |
--------------------------------------------
|   1     |   1     |   0    |    1     | 1         |
--------------------------------------------
|   0     |   0     |   1    |    0     | 1         |
--------------------------------------------
|   0     |   1     |   1    |    1     | 1         |
--------------------------------------------
|   1     |   1     |   1    |    0     | 0         |
--------------------------------------------
It is pretty beautiful that in this table the triple (old_q, r, p) is unique, so we can use p to calculate q. And we are still able to use q to represents "elonBit" because for state 1, (p,q) = (0, 1)
p = (p & q & ~ r) | (~p & q & r)
q = (q & ~r & ~p) | (q & ~r & p) | (~q & r & ~p) | (q & r & p)
After reducing the above equations, we have:
p = q & (p ^ r)
q = p | (q ^ r)
IV. Method 4
public class Solution {
    public int singleNumber(int[] nums) {
       int p = 0;
       int q = 0;
       int r;
       for(int i = 0; i<nums.length; i++){
          r = nums[i]; //this to make it consistent with our analysis. 
          p = q & (p ^ r);
          q = p | (q ^ r);
       }
       return q;
    }
}
After understanding all of this process, we can solve different problems. For example, given an array in which all elements appear 3 times but one element is missing. For example [0,0,0, 1,1, 2, 2, 2, 3, 3, 3] where one "1" is missing.
Since the state 2 is represented as (1,1), hence instead of "return q", we can simply return "p&q" in the above code.

Saturday, August 29, 2015

Single Number

Problem Description
Given an array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
Solutions
I. Using Hash Table
We can solve this problem by simply counting the number of times a number appeared.
Java Code
public class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Boolean> map = new HashMap<Integer, Boolean>();
        for(int i = 0; i<nums.length; i++){
            
            if(!map.containsKey(nums[i])){
                map.put(nums[i], false);
            }else{
                map.put(nums[i], true);
            }
        }
        for(int i: map.keySet()){
            if (!map.get(i)) return i;
        }
        return 0;
    }
}
II. Bit manipulation
In this solution, we have the following observation, given an integer N:
0 ^ N = N
N ^ N = 0
Where ^ is the XOR operator.
public class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for (int i = 0; i<nums.length; i++){
            
            result ^= nums[i];
        }
        return result;
    }
}

Friday, August 28, 2015

Number of 1 Bits

Problem Description
Calculate Hamming Weight of an unsigned integer.
NOTE: Hamming Weight of an unsigned integer is the number of 1 bits in its unsigned representation.
Solution
In order to solve the problem, we iteratively check the right most bit if it is 1, then shift all the bits to the right 1 bit (i.e divide it by 2). The code for this implementation is in part I (java) & part II (C++).

We can also do a trick here. It's good to know that we can drop the rightmost set bit by the operation (n&(n-1)).

It is in part III & IV. In part V, we see different naive implementations.
I. Java
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count = 0;
        
        while(n != 0){
            if((n & 1) == 1) count++;
            n >>>= 1;
        }
        return count;
    }
}
II. C++
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        while(n != 0){
            if(n & 1) count++;
            n >>=1;
        }
        return count;
    }
};
III. Java with (n & (n-1)) Solution
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count = 0;
        while(n!=0){
            n &= (n-1);
            count++;
        }
        return count;
    }
}

IV. C++ with (n & (n-1)) Solution
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
    
        while (n) {
            n &= (n - 1);
            count++;
        }
    
        return count;
    }
};
V. Naive Java Solutions (From Wiki: Hamming Weight with modification)
public class Solution {
    
    public int hammingWeight(int n) {
        return popcount_3(n);   
    }
    // you need to treat n as an unsigned value
    final int m1  = 0x55555555; //binary: 0101...
    final int m2  = 0x33333333; //binary: 00110011..
    final int m4  = 0x0f0f0f0f; //binary:  4 zeros,  4 ones ...
    final int m8  = 0x00ff00ff; //binary:  8 zeros,  8 ones ...
    final int m16 = 0x0000ffff; //binary: 16 zeros, 16 ones ...
    final int hff = 0xffffffff; //binary: all ones
    final int h01 = 0x01010101; //the sum of 256 to the power of 0,1,2,3...
    
    //This is a naive implementation, shown for comparison,
    //and to help in understanding the better functions.
    //It uses 24 arithmetic operations (shift, add, and).
    int popcount_1(int x) {
        x = (x & m1 ) + ((x >>>  1) & m1 ); //put count of each  2 bits into those  2 bits 
        x = (x & m2 ) + ((x >>>  2) & m2 ); //put count of each  4 bits into those  4 bits 
        x = (x & m4 ) + ((x >>>  4) & m4 ); //put count of each  8 bits into those  8 bits 
        x = (x & m8 ) + ((x >>>  8) & m8 ); //put count of each 16 bits into those 16 bits 
        x = (x & m16) + ((x >>> 16) & m16); //put count of each 32 bits into those 32 bits 
        return x;
    }
    
    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with slow multiplication.
    //It uses 17 arithmetic operations.
    int popcount_2(int x) {
        x -= (x >>> 1) & m1;             //put count of each 2 bits into those 2 bits
        x = (x & m2) + ((x >>> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >>> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        x += x >>>  8;  //put count of each 16 bits into their lowest 8 bits
        x += x >>> 16;  //put count of each 32 bits into their lowest 8 bits
        return x & 0x7f;
    }
    
    //This uses fewer arithmetic operations than any other known  
    //implementation on machines with fast multiplication.
    //It uses 12 arithmetic operations, one of which is a multiply.
    int popcount_3(int x) {
        x -= (x >>> 1) & m1;             //put count of each 2 bits into those 2 bits
        x = (x & m2) + ((x >>> 2) & m2); //put count of each 4 bits into those 4 bits 
        x = (x + (x >>> 4)) & m4;        //put count of each 8 bits into those 8 bits 
        return (x * h01)>>>24;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 
    }
}