LeetCode 779.第K个语法符号
题目描述
在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。
给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)
例子:
输入: N = 1, K = 1
输出: 0
输入: N = 2, K = 1
输出: 0
输入: N = 2, K = 2
输出: 1
输入: N = 4, K = 5
输出: 1
解释:
第一行: 0
第二行: 01
第三行: 0110
第四行: 01101001
注意:
N 的范围 [1, 30].
K 的范围 [1, 2^(N-1)].
解题
直接递归
第N行第K个数字由第N-1行第(K+1)/2个数字得来,一直递归下去就能得到最初始的数字,然后从最开始的这个数字出发,一路返回,最终就可以推断出要求的数字。比如上一层返回结果为0,那么如果K是奇数,则返回0,如果K是偶数,则返回1;如果上一层返回结果位1,那么结果相反。
class Solution {
public:
int kthGrammar(int N, int K) {
if (N<=2) return K-1;
return kthGrammar(N-1, (K+1)/2) ? K&1 : 1-(K&1);
}
};
另一种递归
最一开始就可以知道当前行的数字数目是上一行的两倍,而且,有趣的是,当前行的前半段数字与上一行完全相同,后半段是前半段对应位置的翻转(0变为1,1变为0)。那么如果K大于当前行N对应数字总数S的一半,那么第K个数字的值应该是第(K-S/2)个数字的翻转,该数字与第N-1行第(K-S/2)个数字相同,以此同样可以“溯源”,然后返回过程中推导出当前层目标位置的数字。
class Solution {
public:
int kthGrammar(int N, int K) {
if (N<=2) return K-1;
if (K > (1<<(N-2))) return kthGrammar(N-1, K-(1<<(N-2))) ^ 1;
else return kthGrammar(N-1, K);
}
};
更巧妙的循环
上面的分析中得知,当前行的所有数字与下一行前半段是相同的,这一点引申一下就是,对于第N行第K个数字,如果K位于当前行的前半段,那么第N行第K个数字一定等于前面某一行n的第K个数字,此时K位于第n行的后半段,且第n行到第N行的第K个数字都相同。
上面的分析还得知,如果当前行要求的那个数字位于后半段,那么该数字与前半段对应数字存在“翻转”的关系。由此可以简化我们“溯源”的过程:
1. 如果当前行要求的位置的数字位于前半段,则直接跳到对应位置在后半段的行。
2. 如果当前行要求的位置的数字位于后半段(不管是一开始就位于后半段或者由第1步跳到该行),当前行的结果应该是前半段对应位置数字的翻转,而前半段对应数字的求法与当前目标位置的数字求法一致,我们同样可以找到前半段那个对应数字位于后半段的行,以此类推。
对于当前第N行,总数字数目位
2
N
−
1
2^{N-1}
2N−1,如果K位于后半段,那么有
2
N
−
2
+
1
≤
K
≤
2
N
−
1
2^{N-2}+1 \leq K \leq 2^{N-1}
2N−2+1≤K≤2N−1
它对应的前半段数字J有:
J
=
K
−
2
N
−
2
J = K-2^{N-2}
J=K−2N−2
1
≤
J
≤
2
N
−
2
1 \leq J \leq 2^{N-2}
1≤J≤2N−2
如果要保证J同样位于后半段,那么:
2
N
−
3
+
1
≤
J
≤
2
N
−
2
2^{N-3}+1 \leq J \leq 2^{N-2}
2N−3+1≤J≤2N−2
反映在二进制上,就是K对应二进制数的次高位为1。
我们继续做如下推理:如果K对应二进制的次高位为1表示它对应的前半段位置的数字在上一行同样位于后半段。如果K对应二进制的次高位为0,那么它对应的前半段位置的元素在上一行位于前半段,结合上述第1点描述,那么可以直接跳到对应位置在后半段的行,假设这一行为L,那么:
2
L
−
2
+
1
≤
K
≤
2
L
−
1
2^{L-2}+1 \leq K \leq 2^{L-1}
2L−2+1≤K≤2L−1
即二进制的第L或第L-1位为1,这里又出现了翻转,这里结论就可以引申开来:翻转的次数等于二进制中1的个数。
但是以上的推理中存在一个缺陷,那就是第N层第K个数字是否位于后半段时,其最高位的位置或许为N或许为N-1,这样无法统一,而再次观察:
2
N
−
2
+
1
≤
K
≤
2
N
−
1
2^{N-2}+1 \leq K \leq 2^{N-1}
2N−2+1≤K≤2N−1
发现:
2
N
−
2
≤
K
−
1
≤
2
N
−
1
−
1
2^{N-2} \leq K-1 \leq 2^{N-1}-1
2N−2≤K−1≤2N−1−1
其最高位确定在第N-1位,而且这里减去的1将会“顺延”给J,所以结论就是翻转的次数等同于K-1对应的二进制中1的个数。
class Solution {
public:
int kthGrammar(int N, int K) {
int num=0;
--K;
while (K){
if (K&1) ++num;
K>>=1;
}
return num % 2;
}
};