Bit masking is a way to use a single integer as a collection of on/off switches. Each bit in the integer acts like a checkbox: 1 means "on" or "present", 0 means "off" or "absent".
This is useful when you need to track which items from a small set are "active". For example, which letters appear in a word, or which nodes have been visited.
13 in binary is 1101. Reading right to left, slots 0, 2, and 3 are "on" (value 1). Slot 1 is "off" (value 0).Computers store numbers in binary (base 2). Each digit is called a bit and can only be 0 or 1. The rightmost bit is position 0, and positions increase as you move left.
101 = (1 x 4) + (0 x 2) + (1 x 1)1101 = (1 x 8) + (1 x 4) + (0 x 2) + (1 x 1)The expression 1 << n creates a number with only bit n set to 1. This is your main tool for targeting a specific bit position.
1 << 0 = 1 (binary: 0001) - bit 0 is on1 << 1 = 2 (binary: 0010) - bit 1 is on1 << 2 = 4 (binary: 0100) - bit 2 is on1 << 3 = 8 (binary: 1000) - bit 3 is onOnce you can target a bit with left shift, you combine it with these operators to manipulate your mask:
|) turns bits ON: Use mask | (1 << n) to set bit n to 1. Think of it as "add to set".&) checks bits: Use mask & (1 << n) to test if bit n is set. Non-zero result means yes.& ~) turns bits OFF: Use mask & ~(1 << n) to clear bit n. Think of it as "remove from set".To add an item to your set, turn its bit ON using OR. Here is a step-by-step example:
mask = 0 (binary: 0000) - empty set1 << 3 = 8 (binary: 1000)0000 | 1000 = 1000mask = 8 - bit 3 is now ON, meaning item 3 is in the setmask = 0 # empty set
mask |= 1 << 3 # add item 3 -> mask is now 8 (binary: 1000)
mask |= 1 << 1 # add item 1 -> mask is now 10 (binary: 1010)
int mask = 0; // empty set
mask |= 1 << 3; // add item 3 -> mask is now 8 (binary: 1000)
mask |= 1 << 1; // add item 1 -> mask is now 10 (binary: 1010)
int mask = 0; // empty set
mask |= 1 << 3; // add item 3 -> mask is now 8 (binary: 1000)
mask |= 1 << 1; // add item 1 -> mask is now 10 (binary: 1010)
int mask = 0; // empty set
mask |= 1 << 3; // add item 3 -> mask is now 8 (binary: 1000)
mask |= 1 << 1; // add item 1 -> mask is now 10 (binary: 1010)
To check if an item is in your set, use AND. If the result is non-zero, the item is present.
1010)1 << 3 = 8 (binary: 1000)1010 & 1000 = 1000 (non-zero!)1010)1 << 2 = 4 (binary: 0100)1010 & 0100 = 0000 (zero!)mask = 10 # binary: 1010 (items 1 and 3 are in the set)
if mask & (1 << 3): # check item 3
print("item 3 is present") # this runs
if mask & (1 << 2): # check item 2
print("item 2 is present") # this does NOT run
int mask = 10; // binary: 1010 (items 1 and 3 are in the set)
if (mask & (1 << 3)) // check item 3
cout << "item 3 is present"; // this runs
if (mask & (1 << 2)) // check item 2
cout << "item 2 is present"; // this does NOT run
int mask = 10; // binary: 1010 (items 1 and 3 are in the set)
if ((mask & (1 << 3)) != 0) // check item 3
Console.WriteLine("item 3 is present"); // this runs
if ((mask & (1 << 2)) != 0) // check item 2
Console.WriteLine("item 2 is present"); // this does NOT run
int mask = 10; // binary: 1010 (items 1 and 3 are in the set)
if ((mask & (1 << 3)) != 0) // check item 3
System.out.println("item 3 is present"); // this runs
if ((mask & (1 << 2)) != 0) // check item 2
System.out.println("item 2 is present"); // this does NOT run
The real power of bit masking is comparing two sets instantly. If you AND two masks together:
Example: Do sets {1, 3} and {2, 3} share any items?
1010 (bits 1 and 3 are on)1100 (bits 2 and 3 are on)1010 & 1100 = 1000 (non-zero!)A classic problem: given two words, do they share any letters? With bit masking, this becomes trivial.
Example: Do "abc" and "xyz" share any letters?
Example: Do "abc" and "cde" share any letters?
def word_to_mask(word):
mask = 0
for ch in word:
mask |= 1 << (ord(ch) - ord('a'))
return mask
def share_letters(word1, word2):
return word_to_mask(word1) & word_to_mask(word2) != 0
# Example
print(share_letters("abc", "xyz")) # False
print(share_letters("abc", "cde")) # True (share 'c')
int wordToMask(string& word) {
int mask = 0;
for (char c : word)
mask |= 1 << (c - 'a');
return mask;
}
bool shareLetters(string& word1, string& word2) {
return (wordToMask(word1) & wordToMask(word2)) != 0;
}
// Example
cout << shareLetters("abc", "xyz"); // 0 (false)
cout << shareLetters("abc", "cde"); // 1 (true, share 'c')
int WordToMask(string word) {
int mask = 0;
foreach (char c in word)
mask |= 1 << (c - 'a');
return mask;
}
bool ShareLetters(string word1, string word2) {
return (WordToMask(word1) & WordToMask(word2)) != 0;
}
// Example
Console.WriteLine(ShareLetters("abc", "xyz")); // False
Console.WriteLine(ShareLetters("abc", "cde")); // True (share 'c')
int wordToMask(String word) {
int mask = 0;
for (char c : word.toCharArray())
mask |= 1 << (c - 'a');
return mask;
}
boolean shareLetters(String word1, String word2) {
return (wordToMask(word1) & wordToMask(word2)) != 0;
}
// Example
System.out.println(shareLetters("abc", "xyz")); // false
System.out.println(shareLetters("abc", "cde")); // true (share 'c')