首先多扩充几行看看有什么规律:
1: 0
2: 0 1
3: 0 1 1 0
4: 0 1 1 0 1 0 0 1
5: 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0
6: 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
......
发现有以下规律:
n行的
2
n
−
1
2^{n-1}
2n−1个数,与第n+1行的前
2
n
−
1
2^{n-1}
2n−1个数是相同的n行的
2
n
−
1
2^{n-1}
2n−1个数的序数划分为两个区间:
[
1
,
2
n
−
2
]
,
[
2
n
−
2
+
1
,
2
n
−
1
]
[1,2^{n-2}], \ [2^{n-2}+1, 2^{n-1}]
[1,2n−2], [2n−2+1,2n−1],对于第i个数,
i
∈
[
1
,
2
n
−
2
]
i \in [1, 2^{n-2}]
i∈[1,2n−2],第
i
+
2
n
−
2
i+2^{n-2}
i+2n−2个数一定是其取反得到的。通过规律1和2,我们可以做如下操作:
count记录翻转次数,表示下标k进行了几次转换k在第n行中,且
k
∈
[
2
n
−
2
+
1
,
2
n
−
1
]
k \in [2^{n-2}+1, 2^{n-1}]
k∈[2n−2+1,2n−1],我们进行一次进行一次翻转,将k修改为前半段对应的下标,并令count++;k在第n行中,但是下标不在后半区间,那么k一定是在某一行的后半段的区间中,在该行内做转换。这样做成立的原因就是前面行中的数字与后面行中的数字一定是相等的。k = k-1,做翻转的操作其实就是k减去它最高位1所代表的值,直至减至k=0位置。实际上就是记录k-1二进制表示中1的个数。count为奇数时说明翻转了奇数次才变为0,则原来的数为1;反之原来的数为0。实际上就是判断
k-1二进制表示中1是奇数个还是偶数个,奇偶校验?
代码如下:
class Solution {
public:
int kthGrammar(int n, int k) {
int count = 0; //记录反转了几次
k-=1;
while(k > 0){
//每次减去它的lowbit;
k = k - (k&(-k));
count++;
}
return count % 2 == 1 ? 1: 0;
}
};