题目描述
在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。
请找出那个只出现一次的数字。
你可以假设满足条件的数字一定存在
样例
输入:[1,1,1,2,2,2,3,4,4,4]
输出:3
状态转移
本题与前一题思路类似,前一题中,其他数都出现了两次,因此需要的状态转移方式是,如果出现两个1就抵消为0,用一个变量和异或运算即可实现,而本题是需要1出现三次时才会抵消,因此有三种状态,即1出现的次数为3k, 3k + 1, 3k + 2次
逐个位来看,要设计一个两位的状态转移,出现三个1时,循环抵消,出现0时不变,一个变量只能记录两种状态,因此要用两个变量来记录状态,用one和two两个变量来记录1出现次数
00表示1出现3k次,01表示1出现3k + 1次,10表示1出现3k + 2次
真值表
two one x two one
0 0 1 0 1
0 1 1 1 0
1 0 1 0 0
0 0 0 0 0
0 1 0 0 1
1 0 0 1 0
先看one的状态转移方程
one = (~one & ~two & x) | (one & ~two & ~x)
= ~two & ((~one & x) | (one & ~x))
= ~two & (one ^ x)
同理,再用转移后的one来求two的状态转移方程
这里,one为当且仅当1出现次数为3k + 1, tow为当且仅当1出现次数为3k + 2
因此如果题目改为,有一个数出现了两次,则返回two即可
C++ 代码
class Solution {
public:
int findNumberAppearingOnce(vector<int>& nums) {
int one=0,two=0;
for(auto x:nums)
{
one=(one^x)&~two;
two=(two^x)&~one;
}
return one;
}
};
数电即视感
大家在这里,需要注意,先更新低位,然后在更新高位。如果不按顺序的话,需要使用临时变量保留上一阶段的值。不然会有bug
自己用K图画了一下,然后用的最简与或式,就踩了坑,已经更新过one以后,计算two的时候要用更新过的one去画K图化简。。。
而且这道题如果先更新two再用新的two计算one的转移的话,还有状态的冲突,相同的状态会转移到不同的one,
所以还真就按照题解里的最简单。
顺便贴一下我列的与或式的转移方程,我的k相当于是ones
对着式子直接化简我是真的忘光了,就剩下K图还记得怎么用
# NB
two的状态转移是怎么化简出来的?个人太捞了怎么也化不出来。。。
two用的是计算之后的one,也就是最后一列,
大佬,3Q!!我一直用的更新之前的one(第二列),怪不得化不出来…
真的强,这个真值表+状态转移方程
# 这是真的强
大佬太强了