《393. UTF-8 编码验证》题解

在这里插入图片描述

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 编码的工作方式:

字符数字区间(十六进制)UTF-8 序列(二进制)
0000 0000-0000 007F0xxxxxxx
0000 0080-0000 07FF110xxxxx 10xxxxxx
0000 0800-0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF11110xxx 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/utf-8-validation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

首先分析题目的意思,输入会是一个整数的数组,数字的范围是 0 ~ 255,代表的是二进制的 00000000 ~ 11111111,正好是一个字节的二进制表示。可以想象成二进制的字节流。一次收到一个字节。怎么确认这一段数据,是一个有效的 UTF-8 编码的字串呢?

根据题目意思,UTF-8 是一个变长编码的字节流,一个字符可能是 1 个字节 到 4 个字节。如果,我们能够按照 UTF-8 的编码规则,将整个字节流,切割成一个个合法的“字符”,那这个数据就是合法的 UTF-8 串,换句话说,如果有字节是多余的,或者不足的,就不是一个合法的 UTF-8 字符串。

我们可以根据规则来模拟:

  1. 看到一个 0 开头的,我们认为是单字节的字符;
  2. 看到 110 开头的,我们认为是双字节字符,检查跟随的1个字节,是不是 10 开头;
  3. 看到 1110 开头的,我们认为是三字节字符,检查跟随的2个字节,是不是 10 开头;
  4. 看到 11110 开头的,我们认为是四字节字符,检查跟随的3个字节,是不是 10 开头;

除了上述的情况,多出来的,或者缺少的字节,就会导致整个字符串非法。

代码

class Solution:
    def validUtf8(self, data: List[int]) -> bool:
        p = 0
        n = len(data)
        # Python 里将整数转换成二进制字符串
        def binrep(num: int):
            rep = '00000000' + bin(data[p])[2:]
            return rep[len(rep) - 8:]

        while p < n:
            rep = binrep(data[p])
            # 单字节
            if rep.startswith('0'):
                p += 1
            else:
                # 二到四字节
                for i in range(2, 5):
                    prefix = '1' * i + '0'
                    if rep.startswith(prefix):
                        p += 1
                        for j in range(i - 1):
                            if p >= n: return False # 字符不够用了,就是非法字符
                            rep = binrep(data[p])
                            if not rep.startswith('10'):
                                # 不满足跟随字符的格式,就是非法字符
                                return False
                            p += 1
                        break 
                else: # 注意:这里是一个 Python 的编码技巧
                    # 不满足二到四字节的开头字符的格式,就是非法字符
                    return False
        return True

时间复杂度: O ( n ) O(n) O(n) ,从上面代码,我们不难看出,针对每个字节,我们会 尝试 用 1 - 4 字节的开头格式去测试。测试总次数不会超过 n × 4 n \times 4 n×4 次。

空间复杂度: O ( 1 ) O(1) O(1)

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用内容,CSP-J2021复赛有两道题目,分别是分糖果和小熊的果篮。 对于第一题分糖果,题目来源是CCF,难度为入门。根据给出的代码,这是一个基于循环的算法,通过遍历[l,r]区间内的数,计算数对n取模后的最大值。具体的实现细节可以参考引用中的代码。这道题目属于入门级别,比较简单。 第二题是关于小熊的果篮。给定一个长度为n的数组a,其中连续的相同元素被视为一个块,要求按照块的顺序输出每个块的头元素,并删除已输出的元素。具体的实现细节可以参考引用中的代码。这道题目需要使用双链表来处理,时间复杂度为O(n)。 综上所述,CSP-J2021复赛的题目包括分糖果和小熊的果篮,具体的解题思路和代码实现可以参考上述引用内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [[CSP-J 2021]比赛题解](https://blog.csdn.net/weixin_56550385/article/details/126811201)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [新鲜出炉的 CSP-J 2021 复赛题目 题解](https://blog.csdn.net/qq_23109971/article/details/121024436)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Charles@TechBlog

您的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值