开启我的LeetCode刷题日记:393. UTF-8 编码验证

168 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

编程世界总是离不了算法

最近在看框架源码时,会有很多算法的实现逻辑,有时候会感到吃力

于是决定蹭着假期,加强算法和数据结构相关的知识

那怎么提升呢?

其实我知道算法这东西没有捷径,多写多练才能提升,于是我开启我的LeetCode刷题之旅

第一阶段目标是:200道,每天12

为了不乱,本系列文章目录分为三部分:

  1. 今日题目:xxx
  2. 我的思路
  3. 代码实现

今天题目:393. UTF-8 编码验证

给定一个表示数据的整数数组 data ,返回它是否为有效的 UTF-8 编码。

UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:

对于 1 字节 的字符,字节的第一位设为 0 ,后面 7 位为这个符号的 unicode 码。 对于 n 字节 的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为 0 ,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。 这是 UTF-8 编码的工作方式:

Char. number range | UTF-8 octet sequence (hexadecimal) | (binary) --------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 注意:输入是整数数组。只有每个整数的 最低 8 个有效位 用来存储数据。这意味着每个整数只表示 1 字节的数据。

 

示例 1:

输入:data = [197,130,1] 输出:true 解释:数据表示字节序列:11000101 10000010 00000001。 这是有效的 utf-8 编码,为一个 2 字节字符,跟着一个 1 字节字符。 示例 2:

输入:data = [235,140,4] 输出:false 解释:数据表示 8 位的序列: 11101011 10001100 00000100. 前 3 位都是 1 ,第 4 位为 0 表示它是一个 3 字节字符。 下一个字节是开头为 10 的延续字节,这是正确的。 但第二个延续字节不以 10 开头,所以是不符合规则的。  

提示:

1 <= data.length <= 2 * 104 0 <= data[i] <= 255

我的思路

根据题目模拟,从第一个数开始遍历,我们判断每个数的第1位,如果是0代表它只能表示1个字节字符,直接i++继续。如果第1位是1,代表后面的数字要跟当前数字一起组成字符。具体字节取决于当前数字前面有几个1。

注意:

题目说明字符的长度为1 - 4的字节,所以我们要保证当前数前面的1要大于1个,且小于5个。具体用例可以参考: [250,145,145,145,145] 字节为5个,不符合 [128,1,1] 128有1个1,无法代表单字节、多字节 js运算符优先级。(4 & (1 << 7)) === 0 和 4 & (1 << 7) === 0 结果不一样,=== 优先级大于 &

代码实现

/**
 * @param {number[]} data
 * @return {boolean}
 */
var validUtf8 = function(data) {
    // 看第一个字节的首位,是1还是0,0就代表是是一个字节字符。再看下一个字节
    // 如果是1,累计几个1,后面几个字节都要以10开头。

    let i = 0;
    while (i < data.length) {
        const current = data[i];
        // 首位是0, 代表一个字节
        if ((current & (1 << 7)) === 0) {
            i++;
        } else {
            let j = 6; // 注: 本身也算个字节,所以要少算个1
            i++;
            // 可能最后一个字节首位是1,那就不符合条件
            if (i === data.length) {
                return false;
            }
            // 开始从下个字节检查是否以10开头,这过程必须保证current的当前进制位是1
            while ((current & (1 << j)) && i < data.length) {
                const next = data[i];
                if ((next & (1 << 7)) === 0 || (next & (1 << 6)) !== 0) {
                    return false;
                }
                j--;
                i++;
            }
            // 注意审题:可能的长度为1到4字节,所以我们判断j是否小于3(6 - 3),是否等于6 返回false
            if (j < 3 || j === 6) {
                return false
            } 
            // 如果是current当前位置还是1,且i超过data.length。说明多字节字符不符合。
            if ((current & (1 << j)) && i >= data.length) {
                return false
            }
        }
    }
    return true;
};


总结

实现方式其实有很多,这里仅供参考~

由于刚开始刷题,也不知道从哪里刷好,如果前辈们有好的建议,希望不吝赐教,感谢🌹