leetcode:393. UTF-8 编码验证

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

题目解析

题目大意

由1-4(byte)构成一个UTF8字符,生成规则如下:

  • 如果一个UTF8字符由1个字节构成,那么字节第一位是0
  • 如果一个UTF8字符由n(2~4)个字节构成,那么第1个字节前n位(bit)是1,后面n-1个字节前2位是10

给你若干个范围在 [0,255] 的十进制数,即每个数代表 1 个字节,判断这堆字节能否代表一组 UTF8编码。

说人话的 题目意思是:

输入的整数甭管怎么样,都看作8位2进制的数。

  • 从左到右开始模拟,
  • 当前数字如果是0开头的,那么该数字单独成一个1字节字符。
  • 当前数字如果是110开头的,那么该数字需要额外跟着一个10开头的数,构成一个2字节字符。
  • 数字开头有xx个1,后面就需要跟着x-1x−1个10开头的数和它划为一组。
  • 每组数字(几个数一组)都由这组数的开头数决定。
class Solution {
public:
    bool validUtf8(vector<int>& data) {
        int n = data.size();
        //0 <= data[i] <= 255(二进制中为11111111,8bit=1byte),所以每个data[i]占1个字节
        //满足条件的头字节有:0xxxxxxx 110xxxxx 1110xxxx 11110xxx
        for (int i = 0; i < n; ) {
            int t = data[i];
            // 1. 校验头字节
            int j = 7, cnt = 0; //cnt为头字节中开头1的数量
            while(j >= 0 && ((t >> j) & 1) == 1 && ++cnt >= 0) j--;
            //开头要么是0个1(即1个0),要么是2-4个1
            if (cnt == 1 || cnt > 4) return false;
            //2.头字节后面的字节数小于 cnt-1个
            if (i + cnt - 1 >= n) return false;
            //3.判断头字节后的cnt-1个字节前两位开头是否为10
            for (int k = i + 1; k < i + cnt; ++k) {
                if (((data[k] >> 7) & 1) == 1 && ((data[k] >> 6) & 1) == 0) continue;
                return false;
            }
            //4.当前的字符满足规则,继续向后判断
            //如果cnt=0,即为题目说的1字节的字符,则当前data[i]为一个有效的unicode
            if (cnt == 0) i++;
                //cnt不为0,向后移动cnt个字符
            else i += cnt;
        }
        return true;
    }
};
class Solution {
    public boolean validUtf8(int[] data) {
        int i = 0;
        while (i < data.length) {
            // 先判断是几字节字符
            int count = count(data[i]);
            if (count == -1) {
                return false;
            }

            // 再遍历几个数字
            while (--count > 0) {
                if (++i >= data.length) {
                    return false;
                }
                int x = data[i];
                if ((x & 0b11000000) != 0b10000000) {
                    return false;
                }
            }
            
            i++;
        }
        return true;
    }

    private int count(int x) {
        if ((x & 0b10000000) == 0) {
            return 1;
        }
        if ((x & 0b11100000) == 0b11000000) {
            return 2;
        }
        if ((x & 0b11110000) == 0b11100000) {
            return 3;
        }
        if ((x & 0b11111000) == 0b11110000) {
            return 4;
        }
        return -1;
    }
}

分析

每个UTF-8 字符由 1 到 4 个字节组成。我们来仅仅分析data数组中的某一个字符data[i]是否符号规则。

(1)0 <= data[i] <= 255(二进制中为11111111,8bit=1byte),所以每个data[i]占1个字节

(2)对于字符data[i](如下将第1个字节称为头字节,除了第1个字节之外的字节叫做其他字节)

  • 头字节中包含了当前字符的字节数信息,根据头字节计算当前字符字节数的方法如下:
    • 如果头字节的最高位是0,则当前字符由1个字节组成,只有头字节,没有其余字节
    • 如果头字节的最高位是1,则计算头字节从最高位开始的连续的1个个数。如果连续的1的个数为2个到4个,则连续的1个个数表示当前字符的字节数;否则头字节不符合UTF-8编码的规则。当前**data[i]**不是有效的UTF-8编码
    • 综上两种情况,满足条件的头字节有:0xxxxxxx 110xxxxx 1110xxxx 11110xxx
  • 当头字节符合UTF-8编码的规则时,根据头字节得到当前字符的字节数为n,则当前字符包括头字节和n-1个其余**字节*。如果 data 在头字节后面的字节数小于 n - 1,即index+n>data[i].size(),则data不是有效的UTF-8编码
  • 当 data 在头字节后面的字节数大于等于 n - 1时…

分析

根据题意,UTF-8 编码分为两个部分,一个是头部,另外一个是数据部分

对于头部:

  • 头部主要是确定 UTF-8 编码的长度,分为两种:
    • UTF-8 编码的首个字符,以 0b0, 0b110, 0b1110, 0b11110开头分别表示长度为 1-4(0b0->1、0b110->2,0b1110->3,0b11110 ->4) 的编码,注意0b10 开头的是不合理的(例如示例 [145])
    • 非 UTF-8 编码的首个字符(指长度大于 1 的 UTF-8 编码的除第一个字节以外的部分)必须以 0b10开头
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值