思路
首先多扩充几行看看有什么规律:
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
一定是在某一行的后半段的区间中,在该行内做转换。这样做成立的原因就是前面行中的数字与后面行中的数字一定是相等的。 - 每次进行区间翻转时,实际上就是减去区间长度的一半。
- 我们可以令下标从0开始,并令
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;
}
};