🧠
Bitmasking with Predefined Masks for Leading Bytes
💡 This approach uses bitmasks to cleanly identify the number of bytes in a character, improving readability and reducing error-prone comparisons.
Intuition
Use masks to isolate leading bits and compare against known UTF-8 patterns to determine character length, then verify continuation bytes similarly.
Algorithm
- Define masks and expected patterns for 1 to 4 byte UTF-8 characters.
- For each byte, apply masks to determine the character length.
- Check if the following bytes are valid continuation bytes using bitmask.
- Return false if any check fails; otherwise, return true.
💡 Using masks abstracts away bit shifts and makes the code easier to verify and extend.
def validUtf8(data):
masks = [0b10000000, 0b11100000, 0b11110000, 0b11111000]
patterns = [0b00000000, 0b11000000, 0b11100000, 0b11110000]
n = len(data)
i = 0
while i < n:
byte = data[i]
num_bytes = 0
for idx in range(4):
if (byte & masks[idx]) == patterns[idx]:
num_bytes = idx + 1
break
if num_bytes == 0 or num_bytes > 4:
return False
if i + num_bytes > n:
return False
for j in range(i + 1, i + num_bytes):
if (data[j] & 0b11000000) != 0b10000000:
return False
i += num_bytes
return True
# Driver code
if __name__ == '__main__':
print(validUtf8([197, 130, 1])) # True
print(validUtf8([235, 140, 4])) # False
Line Notes
masks = [0b10000000, 0b11100000, 0b11110000, 0b11111000]Masks isolate leading bits for 1-4 byte characters
patterns = [0b00000000, 0b11000000, 0b11100000, 0b11110000]Expected bit patterns for each character length
if (byte & masks[idx]) == patterns[idx]:Check if byte matches the pattern for this character length
if (data[j] & 0b11000000) != 0b10000000:Verify continuation bytes start with '10'
public class Solution {
public boolean validUtf8(int[] data) {
int[] masks = {0b10000000, 0b11100000, 0b11110000, 0b11111000};
int[] patterns = {0b00000000, 0b11000000, 0b11100000, 0b11110000};
int n = data.length;
int i = 0;
while (i < n) {
int byteVal = data[i];
int numBytes = 0;
for (int idx = 0; idx < 4; idx++) {
if ((byteVal & masks[idx]) == patterns[idx]) {
numBytes = idx + 1;
break;
}
}
if (numBytes == 0 || numBytes > 4) return false;
if (i + numBytes > n) return false;
for (int j = i + 1; j < i + numBytes; j++) {
if ((data[j] & 0b11000000) != 0b10000000) return false;
}
i += numBytes;
}
return true;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.validUtf8(new int[]{197, 130, 1})); // true
System.out.println(sol.validUtf8(new int[]{235, 140, 4})); // false
}
}
Line Notes
int[] masks = {0b10000000, 0b11100000, 0b11110000, 0b11111000};Masks for isolating leading bits
int[] patterns = {0b00000000, 0b11000000, 0b11100000, 0b11110000};Expected patterns for each UTF-8 character length
if ((byteVal & masks[idx]) == patterns[idx]) {Check if byte matches a valid UTF-8 leading pattern
if ((data[j] & 0b11000000) != 0b10000000) return false;Check continuation bytes start with '10'
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
bool validUtf8(vector<int>& data) {
int masks[4] = {0b10000000, 0b11100000, 0b11110000, 0b11111000};
int patterns[4] = {0b00000000, 0b11000000, 0b11100000, 0b11110000};
int n = data.size();
int i = 0;
while (i < n) {
int byteVal = data[i];
int numBytes = 0;
for (int idx = 0; idx < 4; idx++) {
if ((byteVal & masks[idx]) == patterns[idx]) {
numBytes = idx + 1;
break;
}
}
if (numBytes == 0 || numBytes > 4) return false;
if (i + numBytes > n) return false;
for (int j = i + 1; j < i + numBytes; j++) {
if ((data[j] & 0b11000000) != 0b10000000) return false;
}
i += numBytes;
}
return true;
}
};
int main() {
Solution sol;
vector<int> data1 = {197, 130, 1};
vector<int> data2 = {235, 140, 4};
cout << boolalpha << sol.validUtf8(data1) << endl; // true
cout << boolalpha << sol.validUtf8(data2) << endl; // false
return 0;
}
Line Notes
int masks[4] = {0b10000000, 0b11100000, 0b11110000, 0b11111000};Masks to isolate leading bits for UTF-8 bytes
int patterns[4] = {0b00000000, 0b11000000, 0b11100000, 0b11110000};Expected bit patterns for each character length
if ((byteVal & masks[idx]) == patterns[idx]) {Check if the byte matches a valid UTF-8 leading byte pattern
if ((data[j] & 0b11000000) != 0b10000000) return false;Verify continuation bytes start with '10'
var validUtf8 = function(data) {
const masks = [0b10000000, 0b11100000, 0b11110000, 0b11111000];
const patterns = [0b00000000, 0b11000000, 0b11100000, 0b11110000];
let n = data.length;
let i = 0;
while (i < n) {
let byte = data[i];
let numBytes = 0;
for (let idx = 0; idx < 4; idx++) {
if ((byte & masks[idx]) === patterns[idx]) {
numBytes = idx + 1;
break;
}
}
if (numBytes === 0 || numBytes > 4) return false;
if (i + numBytes > n) return false;
for (let j = i + 1; j < i + numBytes; j++) {
if ((data[j] & 0b11000000) !== 0b10000000) return false;
}
i += numBytes;
}
return true;
};
// Test cases
console.log(validUtf8([197, 130, 1])); // true
console.log(validUtf8([235, 140, 4])); // false
Line Notes
const masks = [0b10000000, 0b11100000, 0b11110000, 0b11111000];Masks to isolate leading bits for UTF-8 bytes
const patterns = [0b00000000, 0b11000000, 0b11100000, 0b11110000];Expected bit patterns for each UTF-8 character length
if ((byte & masks[idx]) === patterns[idx]) {Check if the byte matches a valid UTF-8 leading byte pattern
if ((data[j] & 0b11000000) !== 0b10000000) return false;Verify continuation bytes start with '10'
We iterate through the array once, using bitmask operations which are constant time.
💡 For large inputs, this approach is just as efficient as brute force but easier to maintain.
Interview Verdict: Accepted
This approach is preferred for clarity and maintainability while maintaining optimal performance.