LeetCode137 只出现一次的数字 II(状态自动机+数字逻辑代数)

题目链接:leetcode137

题目大意

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

示例 1:
输入: [2,2,3,2]
输出: 3

示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99

解题思路

朴素算法

我们可以利用一个 HashSet 统计每个数字出现的次数,于是就可以在时间复杂度与空间复杂度均为 O ( n ) O(n) O(n)下找到答案

状态自动机

根据这串数字的特点我们可以发现,对于序列中任意一种数字,不是出现1次就是出现3次。那么,此时考虑其二进制下每一位的特征,我们把所有数字按位合并(这里的合并指计算出每位「1」的个数),容易得出对于任意一位「1」的个数只有0、1、3、4四种状态。显然,每位的状态对应到答案的映射情况为:如果状态为0、3则对应0;否则对应1。

根据上述的算法,我们可以得到答案,但统计的逻辑在线性时间下不太容易做到,此时我们引入状态自动机的方法。因为对于没一位的输入非0即1,但对于状态的设计我们需要修改一下,以便于自动机状态的切换。设计如下:

在这里插入图片描述

我们发现这种状态图中「2」的状态不可能到达,或者说最终不可能停留在状态2,于是可以优化一下状态图:

在这里插入图片描述

但我们发现,状态3和状态0实际上是等价的,因为状态0输入1后也能转移到结果为1的状态,除此之外,整个状态转移的步长最多为4,所以原本4 – > 3的状态转移实际上最多只会走一次,那么,我们就可以讲「0」和「3」合并,但为了状态表述直观,我们重新处理一下状态,我们用两位二进制数表示三个状态:

在这里插入图片描述

根据状态转移我们可以列出真值表,并且根据真值表可以写出成真与或式,然后化简即可:

在这里插入图片描述

在这里插入图片描述

化简后对于x由于需要使用到上一个y,但此时y已经更新,所以可能需要用到中间变量存储(对于如果先计算y同样存在这个问题),显然有些麻烦,于是我们可以换个变量,把更新后的y带入到x表达式中,作为它的因变量:

在这里插入图片描述
此时,得到两个类似的式子。同理,化简逻辑的方式还有很多,例如卡诺图、根据真值表一步步枚举化简等方式,就不一一列举。注意,最终的答案根据状态自动机我们可以发现,和y相关,所以输出y的所表示的每个二进制位的数即可。(由于计算机内部采用二进制运算,所以可以所有的位同时进行状态转移)

代码实现

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int x = 0, y = 0;
        for (int i = 0; i < nums.size(); i++) {
            y = y ^ nums[i] & ~x;
            x = x ^ nums[i] & ~y;
        }
        return y;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值